@torrent-tv/proxy 2.101.25 → 2.101.27
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
|
@@ -75,10 +75,15 @@ export async function handleApiSourceFilesGet(req, reply, { sourceRegistry, torr
|
|
|
75
75
|
if (consumerId) viewers?.visibleFiles(consumerId, sourceKey, contents.items.map(item => item.fileIndex));
|
|
76
76
|
const generationRaw = typeof req.query?.generation === "string" ? req.query.generation : "";
|
|
77
77
|
const generation = generationRaw !== "" && /^\d+$/.test(generationRaw) ? Number(generationRaw) : null;
|
|
78
|
+
const linkMbps = Number(req.query?.linkMbps);
|
|
79
|
+
const linkSampleAgeMs = Number(req.query?.linkSampleAgeMs);
|
|
80
|
+
const earlyLink = Number.isFinite(linkMbps) && linkMbps > 0
|
|
81
|
+
? { linkMbps, linkSampleAgeMs: Number.isFinite(linkSampleAgeMs) && linkSampleAgeMs >= 0 ? linkSampleAgeMs : null }
|
|
82
|
+
: {};
|
|
78
83
|
const files = contents.files().map(file => {
|
|
79
84
|
if (!consumerId || !Number.isSafeInteger(generation) || file.kind !== "video" || file.excludedReason) return file;
|
|
80
85
|
const waitEstimate = viewerRequests?.waitEstimateForFile?.({ consumerId, generation, sourceKey,
|
|
81
|
-
fileIndex: file.fileIndex, fileName: file.relativePath ?? file.name, fileLength: file.length }) ?? null;
|
|
86
|
+
fileIndex: file.fileIndex, fileName: file.relativePath ?? file.name, fileLength: file.length, ...earlyLink }) ?? null;
|
|
82
87
|
return waitEstimate ? { ...file, waitEstimate } : file;
|
|
83
88
|
});
|
|
84
89
|
return reply.send({
|
|
@@ -37,9 +37,14 @@ export async function handleApiSourceStatsGet(req, reply, { sourceRegistry, torr
|
|
|
37
37
|
const requestedFileName = typeof req.query.fileName === "string" ? req.query.fileName : "";
|
|
38
38
|
const requestedFileLength = typeof req.query.fileLength === "string" && /^\d+$/.test(req.query.fileLength)
|
|
39
39
|
? Number(req.query.fileLength) : null;
|
|
40
|
+
const linkMbps = Number(req.query.linkMbps);
|
|
41
|
+
const linkSampleAgeMs = Number(req.query.linkSampleAgeMs);
|
|
42
|
+
const earlyLink = Number.isFinite(linkMbps) && linkMbps > 0
|
|
43
|
+
? { linkMbps, linkSampleAgeMs: Number.isFinite(linkSampleAgeMs) && linkSampleAgeMs >= 0 ? linkSampleAgeMs : null }
|
|
44
|
+
: {};
|
|
40
45
|
const initialEstimate = () => consumerId && Number.isSafeInteger(generation) && Number.isSafeInteger(fileIndex)
|
|
41
46
|
? viewerRequests?.waitEstimateForFile?.({ consumerId, generation, sourceKey, fileIndex,
|
|
42
|
-
fileName: requestedFileName, fileLength: requestedFileLength }) ?? null
|
|
47
|
+
fileName: requestedFileName, fileLength: requestedFileLength, ...earlyLink }) ?? null
|
|
43
48
|
: null;
|
|
44
49
|
|
|
45
50
|
let torrent;
|
|
@@ -137,7 +142,7 @@ export async function handleApiSourceStatsGet(req, reply, { sourceRegistry, torr
|
|
|
137
142
|
|
|
138
143
|
const waitEstimate = consumerId && Number.isSafeInteger(generation)
|
|
139
144
|
? viewerRequests?.waitEstimateForSource?.({ consumerId, generation, sourceKey, fileIndex,
|
|
140
|
-
fileName: torrent.files?.[fileIndex]?.name ?? requestedFileName, fileLength: requestedFileLength, stats }) ?? null
|
|
145
|
+
fileName: torrent.files?.[fileIndex]?.name ?? requestedFileName, fileLength: requestedFileLength, stats, ...earlyLink }) ?? null
|
|
141
146
|
: null;
|
|
142
147
|
return reply.send(waitEstimate ? { ...stats, waitEstimate } : stats);
|
|
143
148
|
}
|
|
@@ -280,6 +280,8 @@ export class LocalObservations {
|
|
|
280
280
|
fileLength: positive(features.fileLength),
|
|
281
281
|
downloadSpeed: nonNegative(features.downloadSpeed),
|
|
282
282
|
linkMbps: nonNegative(features.linkMbps),
|
|
283
|
+
linkSampleAgeMs: nonNegative(features.linkSampleAgeMs),
|
|
284
|
+
preparedOutputBytes: nonNegative(features.preparedOutputBytes),
|
|
283
285
|
headerRemaining: nonNegative(features.headerRemaining),
|
|
284
286
|
peers: nonNegative(features.peers),
|
|
285
287
|
at: this.#now()
|
|
@@ -302,7 +304,8 @@ export class LocalObservations {
|
|
|
302
304
|
const readings = this.#entries.firstFrame[this.#firstFrameKey()] ?? [];
|
|
303
305
|
const candidates = readings.filter(reading => reading.extension === (features.extension ?? ""));
|
|
304
306
|
if (candidates.length === 0) return null;
|
|
305
|
-
const dimensions = ["fileLength", "downloadSpeed", "linkMbps", "
|
|
307
|
+
const dimensions = ["fileLength", "downloadSpeed", "linkMbps", "linkSampleAgeMs", "preparedOutputBytes",
|
|
308
|
+
"headerRemaining", "peers"];
|
|
306
309
|
const scales = new Map(dimensions.map(key => [key, median(candidates
|
|
307
310
|
.map(reading => nonNegative(reading[key]))
|
|
308
311
|
.filter(value => value !== null && value > 0))]));
|
|
@@ -106,7 +106,7 @@ export class ViewerRequests {
|
|
|
106
106
|
* @param {{ consumerId: string, generation: number, sourceKey: string, fileIndex: number, fileName: string, fileLength: number }} params
|
|
107
107
|
* @returns {object | null}
|
|
108
108
|
*/
|
|
109
|
-
waitEstimateForFile({ consumerId, generation, sourceKey, fileIndex, fileName, fileLength }) {
|
|
109
|
+
waitEstimateForFile({ consumerId, generation, sourceKey, fileIndex, fileName, fileLength, linkMbps, linkSampleAgeMs }) {
|
|
110
110
|
if (!consumerId || !Number.isSafeInteger(generation) || typeof sourceKey !== "string" ||
|
|
111
111
|
!Number.isSafeInteger(fileIndex) || fileIndex < 0) return null;
|
|
112
112
|
const waitId = `${sourceKey}:${fileIndex}:${generation}`;
|
|
@@ -116,7 +116,10 @@ export class ViewerRequests {
|
|
|
116
116
|
extension,
|
|
117
117
|
fileLength,
|
|
118
118
|
downloadSpeed: null,
|
|
119
|
-
linkMbps:
|
|
119
|
+
linkMbps: Number.isFinite(linkMbps) && linkMbps > 0
|
|
120
|
+
? linkMbps : this.#host.viewers.get(consumerId)?.linkReading()?.linkMbps,
|
|
121
|
+
linkSampleAgeMs: Number.isFinite(linkSampleAgeMs) && linkSampleAgeMs >= 0 ? linkSampleAgeMs : null,
|
|
122
|
+
preparedOutputBytes: this.#preparedOutputBytes(sourceKey, fileIndex),
|
|
120
123
|
headerRemaining: null,
|
|
121
124
|
peers: null
|
|
122
125
|
};
|
|
@@ -144,7 +147,8 @@ export class ViewerRequests {
|
|
|
144
147
|
* @param {{ consumerId: string, generation: number, sourceKey: string, fileIndex: number | null, fileName?: string, fileLength?: number | null, stats?: object }} params
|
|
145
148
|
* @returns {object | null}
|
|
146
149
|
*/
|
|
147
|
-
waitEstimateForSource({ consumerId, generation, sourceKey, fileIndex, fileName = "", fileLength = null,
|
|
150
|
+
waitEstimateForSource({ consumerId, generation, sourceKey, fileIndex, fileName = "", fileLength = null,
|
|
151
|
+
stats = {}, linkMbps, linkSampleAgeMs }) {
|
|
148
152
|
if (!consumerId || !Number.isSafeInteger(generation) || typeof sourceKey !== "string") return null;
|
|
149
153
|
const waitId = `${sourceKey}:${fileIndex ?? "all"}:${generation}`;
|
|
150
154
|
const key = `${consumerId}:${waitId}`;
|
|
@@ -154,7 +158,11 @@ export class ViewerRequests {
|
|
|
154
158
|
fileLength: Number.isFinite(stats?.fileLength) && stats.fileLength > 0
|
|
155
159
|
? stats.fileLength : fileLength,
|
|
156
160
|
downloadSpeed: stats?.downloadSpeed,
|
|
157
|
-
linkMbps:
|
|
161
|
+
linkMbps: Number.isFinite(linkMbps) && linkMbps > 0
|
|
162
|
+
? linkMbps : this.#host.viewers.get(consumerId)?.linkReading()?.linkMbps,
|
|
163
|
+
linkSampleAgeMs: Number.isFinite(linkSampleAgeMs) && linkSampleAgeMs >= 0 ? linkSampleAgeMs : null,
|
|
164
|
+
preparedOutputBytes: Number.isSafeInteger(fileIndex) && fileIndex >= 0
|
|
165
|
+
? this.#preparedOutputBytes(sourceKey, fileIndex) : null,
|
|
158
166
|
headerRemaining: Number.isFinite(stats?.headerBytes) && Number.isFinite(stats?.headerDownloadedBytes)
|
|
159
167
|
? Math.max(0, stats.headerBytes - stats.headerDownloadedBytes) : null,
|
|
160
168
|
peers: stats?.connectedPeers ?? stats?.numPeers
|
|
@@ -216,6 +224,23 @@ export class ViewerRequests {
|
|
|
216
224
|
return true;
|
|
217
225
|
}
|
|
218
226
|
|
|
227
|
+
#preparedOutputBytes(sourceKey, fileIndex) {
|
|
228
|
+
const outputs = this.#host.outputs;
|
|
229
|
+
const segmentStore = this.#host.segmentStore;
|
|
230
|
+
if (!outputs?.values || !segmentStore?.sizesOf) return null;
|
|
231
|
+
let bytes = 0;
|
|
232
|
+
const seen = new Set();
|
|
233
|
+
for (const output of outputs.values()) {
|
|
234
|
+
if (output.file?.sourceKey !== sourceKey || output.file?.fileIndex !== fileIndex || !output.outputKey) continue;
|
|
235
|
+
if (seen.has(output.outputKey)) continue;
|
|
236
|
+
seen.add(output.outputKey);
|
|
237
|
+
for (const size of segmentStore.sizesOf(output.outputKey).values()) {
|
|
238
|
+
if (Number.isFinite(size) && size > 0) bytes += size;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return bytes;
|
|
242
|
+
}
|
|
243
|
+
|
|
219
244
|
#pruneEarlyWaits() {
|
|
220
245
|
const cutoff = Date.now() - 30 * 60_000;
|
|
221
246
|
for (const [key, value] of this.#earlyWaits) {
|