@torrent-tv/proxy 2.9.74 → 2.9.75
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 +374 -369
- package/bin/cli.js +8 -0
- package/package.json +1 -1
- package/server.js +229 -228
- package/services/piece-store/shared-piece-store.js +107 -6
- package/services/torrent-worker/client.js +264 -264
- package/services/torrent-worker/pool-adapter.js +179 -179
- package/services/torrent-worker/worker.js +37 -1
- package/test/shared-piece-store.test.js +76 -0
package/bin/cli.js
CHANGED
|
@@ -74,6 +74,7 @@ program
|
|
|
74
74
|
.option("--no-transcode-audio", "Disable optional HLS AAC audio transcoding")
|
|
75
75
|
.option("--no-port-mapping", "Disable automatic UPnP/NAT-PMP port mapping")
|
|
76
76
|
.option("--max-disk-bytes <bytes>", "Cap total downloaded torrent data (0 = disabled; default min(10GB, half free disk))")
|
|
77
|
+
.option("--memory-bytes <bytes>", "Per-torrent budget for pieces kept in memory before spilling to disk (default 512MB)")
|
|
77
78
|
.option("--ffmpeg-bin <path>", "Path to ffmpeg binary")
|
|
78
79
|
.option(
|
|
79
80
|
"--segment-format <format>",
|
|
@@ -108,6 +109,12 @@ const maxDiskBytes =
|
|
|
108
109
|
options.maxDiskBytes !== undefined && Number.isFinite(Number(options.maxDiskBytes)) && Number(options.maxDiskBytes) >= 0
|
|
109
110
|
? Number(options.maxDiskBytes)
|
|
110
111
|
: undefined;
|
|
112
|
+
// Per-torrent memory budget for resident pieces. Pieces past it spill to disk
|
|
113
|
+
// rather than being lost, so a small value costs read latency, never data.
|
|
114
|
+
const memoryBytes =
|
|
115
|
+
options.memoryBytes !== undefined && Number.isFinite(Number(options.memoryBytes)) && Number(options.memoryBytes) > 0
|
|
116
|
+
? Number(options.memoryBytes)
|
|
117
|
+
: undefined;
|
|
111
118
|
const bundledFfmpegBin = typeof ffmpegStatic === "string" ? ffmpegStatic : "";
|
|
112
119
|
const ffmpegBin = options.ffmpegBin ? String(options.ffmpegBin) : bundledFfmpegBin || "ffmpeg";
|
|
113
120
|
|
|
@@ -265,6 +272,7 @@ try {
|
|
|
265
272
|
transcodeAudio,
|
|
266
273
|
ffmpegBin,
|
|
267
274
|
maxDiskBytes,
|
|
275
|
+
memoryBytes,
|
|
268
276
|
segmentFormat: options.segmentFormat
|
|
269
277
|
});
|
|
270
278
|
app = started.app;
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -1,228 +1,229 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Proxy HTTP server bootstrap.
|
|
3
|
-
*
|
|
4
|
-
* Creates and configures the Fastify application, registers all routes and
|
|
5
|
-
* plugins, then starts listening on the first available port at or above the
|
|
6
|
-
* requested one.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import Fastify from "fastify";
|
|
10
|
-
import fastifyCors from "@fastify/cors";
|
|
11
|
-
import fastifyHelmet from "@fastify/helmet";
|
|
12
|
-
import fastifyStatic from "@fastify/static";
|
|
13
|
-
import getPort from "get-port";
|
|
14
|
-
import path from "node:path";
|
|
15
|
-
import { createRequire } from "node:module";
|
|
16
|
-
import { fileURLToPath } from "node:url";
|
|
17
|
-
import { handleHealthGet } from "./routes/health/get.js";
|
|
18
|
-
import { handleHealthzGet } from "./routes/healthz/get.js";
|
|
19
|
-
import { handleApiSourcesPost } from "./routes/api/sources/post.js";
|
|
20
|
-
import { handleApiSourceStatsGet } from "./routes/api/sources/stats/get.js";
|
|
21
|
-
import { handleApiSourceFilesGet } from "./routes/api/sources/files/get.js";
|
|
22
|
-
import { handleApiPlaybackPlanPost } from "./routes/api/playback-plan/post.js";
|
|
23
|
-
import { handleApiSubtitlesGet } from "./routes/api/subtitles/get.js";
|
|
24
|
-
import { handleApiTranscodeSessionsPost } from "./routes/api/transcode-sessions/post.js";
|
|
25
|
-
import { handleApiTranscodeSessionsProgressGet } from "./routes/api/transcode-sessions/progress/get.js";
|
|
26
|
-
import { handleApiTranscodeSessionReleasePost } from "./routes/api/transcode-sessions/release/post.js";
|
|
27
|
-
import { handleApiTranscodeSessionNetReportPost } from "./routes/api/transcode-sessions/net-report/post.js";
|
|
28
|
-
import { handleApiTranscodeSessionSeekPost } from "./routes/api/transcode-sessions/seek/post.js";
|
|
29
|
-
import { handleStreamGet } from "./routes/stream/get.js";
|
|
30
|
-
import { handleTranscodeSessionFileGet } from "./routes/transcode/session-file/get.js";
|
|
31
|
-
import { createSourceRegistry } from "./store/source-registry.js";
|
|
32
|
-
import { WorkerTorrentPool } from "./services/torrent-worker/pool-adapter.js";
|
|
33
|
-
import { HlsSessionManager } from "./services/hls-session-manager.js";
|
|
34
|
-
import { createPlaybackPlanner } from "./services/playback-planner.js";
|
|
35
|
-
import { detectVideoEncoder, benchmarkSoftwarePresets, detectTonemapSupport } from "./services/hwaccel.js";
|
|
36
|
-
import { logger } from "./utils/logger.js";
|
|
37
|
-
|
|
38
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
39
|
-
const __dirname = path.dirname(__filename);
|
|
40
|
-
const require = createRequire(import.meta.url);
|
|
41
|
-
const { version } = require("./package.json");
|
|
42
|
-
const publicRoot = path.resolve(__dirname, "./public");
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Build a list of candidate port numbers starting at `startPort`.
|
|
46
|
-
*
|
|
47
|
-
* @param {number} startPort
|
|
48
|
-
* @param {number} [maxAttempts=51]
|
|
49
|
-
* @returns {number[]}
|
|
50
|
-
*/
|
|
51
|
-
function buildPortCandidates(startPort, maxAttempts = 51) {
|
|
52
|
-
const ports = [];
|
|
53
|
-
for (let index = 0; index < maxAttempts; index += 1) {
|
|
54
|
-
ports.push(startPort + index);
|
|
55
|
-
}
|
|
56
|
-
return ports;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @typedef {Object} ProxyServerOptions
|
|
61
|
-
* @property {string} host - Bind host (e.g. "127.0.0.1" or "0.0.0.0").
|
|
62
|
-
* @property {number} port - Preferred listen port.
|
|
63
|
-
* @property {boolean} transcodeAudio - Whether HLS audio transcoding is enabled.
|
|
64
|
-
* @property {string} ffmpegBin - Path to the ffmpeg executable.
|
|
65
|
-
* @property {number} [maxDiskBytes] - Global disk cap for torrent data (undefined = pool default).
|
|
66
|
-
* @property {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
app.get("/
|
|
169
|
-
app.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
await
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file Proxy HTTP server bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* Creates and configures the Fastify application, registers all routes and
|
|
5
|
+
* plugins, then starts listening on the first available port at or above the
|
|
6
|
+
* requested one.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import Fastify from "fastify";
|
|
10
|
+
import fastifyCors from "@fastify/cors";
|
|
11
|
+
import fastifyHelmet from "@fastify/helmet";
|
|
12
|
+
import fastifyStatic from "@fastify/static";
|
|
13
|
+
import getPort from "get-port";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { createRequire } from "node:module";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { handleHealthGet } from "./routes/health/get.js";
|
|
18
|
+
import { handleHealthzGet } from "./routes/healthz/get.js";
|
|
19
|
+
import { handleApiSourcesPost } from "./routes/api/sources/post.js";
|
|
20
|
+
import { handleApiSourceStatsGet } from "./routes/api/sources/stats/get.js";
|
|
21
|
+
import { handleApiSourceFilesGet } from "./routes/api/sources/files/get.js";
|
|
22
|
+
import { handleApiPlaybackPlanPost } from "./routes/api/playback-plan/post.js";
|
|
23
|
+
import { handleApiSubtitlesGet } from "./routes/api/subtitles/get.js";
|
|
24
|
+
import { handleApiTranscodeSessionsPost } from "./routes/api/transcode-sessions/post.js";
|
|
25
|
+
import { handleApiTranscodeSessionsProgressGet } from "./routes/api/transcode-sessions/progress/get.js";
|
|
26
|
+
import { handleApiTranscodeSessionReleasePost } from "./routes/api/transcode-sessions/release/post.js";
|
|
27
|
+
import { handleApiTranscodeSessionNetReportPost } from "./routes/api/transcode-sessions/net-report/post.js";
|
|
28
|
+
import { handleApiTranscodeSessionSeekPost } from "./routes/api/transcode-sessions/seek/post.js";
|
|
29
|
+
import { handleStreamGet } from "./routes/stream/get.js";
|
|
30
|
+
import { handleTranscodeSessionFileGet } from "./routes/transcode/session-file/get.js";
|
|
31
|
+
import { createSourceRegistry } from "./store/source-registry.js";
|
|
32
|
+
import { WorkerTorrentPool } from "./services/torrent-worker/pool-adapter.js";
|
|
33
|
+
import { HlsSessionManager } from "./services/hls-session-manager.js";
|
|
34
|
+
import { createPlaybackPlanner } from "./services/playback-planner.js";
|
|
35
|
+
import { detectVideoEncoder, benchmarkSoftwarePresets, detectTonemapSupport } from "./services/hwaccel.js";
|
|
36
|
+
import { logger } from "./utils/logger.js";
|
|
37
|
+
|
|
38
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
39
|
+
const __dirname = path.dirname(__filename);
|
|
40
|
+
const require = createRequire(import.meta.url);
|
|
41
|
+
const { version } = require("./package.json");
|
|
42
|
+
const publicRoot = path.resolve(__dirname, "./public");
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build a list of candidate port numbers starting at `startPort`.
|
|
46
|
+
*
|
|
47
|
+
* @param {number} startPort
|
|
48
|
+
* @param {number} [maxAttempts=51]
|
|
49
|
+
* @returns {number[]}
|
|
50
|
+
*/
|
|
51
|
+
function buildPortCandidates(startPort, maxAttempts = 51) {
|
|
52
|
+
const ports = [];
|
|
53
|
+
for (let index = 0; index < maxAttempts; index += 1) {
|
|
54
|
+
ports.push(startPort + index);
|
|
55
|
+
}
|
|
56
|
+
return ports;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @typedef {Object} ProxyServerOptions
|
|
61
|
+
* @property {string} host - Bind host (e.g. "127.0.0.1" or "0.0.0.0").
|
|
62
|
+
* @property {number} port - Preferred listen port.
|
|
63
|
+
* @property {boolean} transcodeAudio - Whether HLS audio transcoding is enabled.
|
|
64
|
+
* @property {string} ffmpegBin - Path to the ffmpeg executable.
|
|
65
|
+
* @property {number} [maxDiskBytes] - Global disk cap for torrent data (undefined = pool default).
|
|
66
|
+
* @property {number} [memoryBytes] - Per-torrent budget for pieces held in memory (undefined = store default).
|
|
67
|
+
* @property {string} [segmentFormat] - HLS output container: "fmp4" (default) or "mpegts".
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create, configure, and start the proxy HTTP server.
|
|
72
|
+
*
|
|
73
|
+
* @param {ProxyServerOptions} options
|
|
74
|
+
* @returns {Promise<{ app: import("fastify").FastifyInstance, port: number }>}
|
|
75
|
+
*/
|
|
76
|
+
export async function startProxyServer({ host, port, transcodeAudio, ffmpegBin, maxDiskBytes, memoryBytes, segmentFormat }) {
|
|
77
|
+
const app = Fastify({
|
|
78
|
+
// No practical body-size limit — the proxy server is localhost-only and
|
|
79
|
+
// receives torrent source payloads that may be arbitrarily large.
|
|
80
|
+
bodyLimit: 256 * 1024 * 1024 // 256 MB
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await app.register(fastifyHelmet, {
|
|
84
|
+
// Proxy serves media to a different origin (registry UI), so CORP must allow cross-origin usage.
|
|
85
|
+
crossOriginResourcePolicy: {
|
|
86
|
+
policy: "cross-origin"
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
await app.register(fastifyCors, {
|
|
90
|
+
origin: true,
|
|
91
|
+
methods: ["GET", "POST", "OPTIONS"],
|
|
92
|
+
allowedHeaders: ["Content-Type", "Range"]
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Allow browser requests from an HTTPS page to this private-network proxy
|
|
96
|
+
// without triggering Chromium's Private Network Access permission prompt.
|
|
97
|
+
app.addHook("onRequest", async (_req, reply) => {
|
|
98
|
+
reply.header("Access-Control-Allow-Private-Network", "true");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const sourceRegistry = createSourceRegistry(200);
|
|
102
|
+
// The torrent runs on its own thread. Profiling a live seek (2026-08-02)
|
|
103
|
+
// found the main thread ~85% occupied by WebTorrent — buffer concatenation
|
|
104
|
+
// ~15%, wire updates ~9%, garbage collection ~5% — while three of four cores
|
|
105
|
+
// idled. Serving a segment shared that thread, so reading an already-finished
|
|
106
|
+
// 10 MB file took 12-23 s against 125 ms to hand it to the channel. The
|
|
107
|
+
// adapter keeps TorrentPool's interface, so nothing downstream changed.
|
|
108
|
+
const torrentPool = new WorkerTorrentPool({ maxDiskBytes, memoryBytes });
|
|
109
|
+
const selectedPort = await getPort({
|
|
110
|
+
port: buildPortCandidates(port)
|
|
111
|
+
});
|
|
112
|
+
// Auto-detect the best available H.264 encoder (hardware-accelerated or
|
|
113
|
+
// software) once at startup, with a real test-encode and graceful fallback.
|
|
114
|
+
// Only needed when transcoding can occur.
|
|
115
|
+
const videoEncoder = transcodeAudio
|
|
116
|
+
? await detectVideoEncoder({ ffmpegBin, logger })
|
|
117
|
+
: null;
|
|
118
|
+
// For software libx264, benchmark preset throughput once at startup so the
|
|
119
|
+
// session manager can pick the highest-quality preset that still encodes each
|
|
120
|
+
// stream faster than realtime. Hardware encoders use their own fixed preset.
|
|
121
|
+
const softwarePresetBenchmark = videoEncoder?.kind === "software"
|
|
122
|
+
? await benchmarkSoftwarePresets({ ffmpegBin, logger })
|
|
123
|
+
: null;
|
|
124
|
+
// Whether this ffmpeg build can tone-map HDR→SDR (zscale + tonemap filters).
|
|
125
|
+
// Detected once; the session manager applies the tonemap chain only for HDR
|
|
126
|
+
// sources on the software path when available.
|
|
127
|
+
const tonemapSupported = transcodeAudio
|
|
128
|
+
? await detectTonemapSupport({ ffmpegBin, logger })
|
|
129
|
+
: false;
|
|
130
|
+
const hlsSessionManager = new HlsSessionManager({
|
|
131
|
+
enabled: transcodeAudio,
|
|
132
|
+
ffmpegBin,
|
|
133
|
+
localBindHost: host,
|
|
134
|
+
localPort: selectedPort,
|
|
135
|
+
videoEncoder,
|
|
136
|
+
softwarePresetBenchmark,
|
|
137
|
+
tonemapSupported,
|
|
138
|
+
segmentFormatId: segmentFormat,
|
|
139
|
+
// Live download stats accessor for the realtime budget: lets it tell a
|
|
140
|
+
// CPU-bound transcode from a download-starved input before downscaling.
|
|
141
|
+
getSourceStats: async (sourceKey, fileIndex) => {
|
|
142
|
+
const record = sourceRegistry.get(sourceKey);
|
|
143
|
+
if (!record) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const torrent = await torrentPool.getTorrent(record.sourceType, record.source);
|
|
148
|
+
// Awaited for the same reason as the stats route: this now crosses a
|
|
149
|
+
// thread boundary and returns a promise.
|
|
150
|
+
return await torrentPool.getFileStats(torrent, Number.isInteger(fileIndex) ? fileIndex : null);
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
// Reuse the media info the planner already probed for this file (same
|
|
156
|
+
// ffmpeg scan) so createSession skips its own probe. Late-bound: invoked
|
|
157
|
+
// only at session-create time, after playbackPlanner is initialised.
|
|
158
|
+
getCachedMediaInfo: (params) => playbackPlanner.getCachedMediaInfo(params)
|
|
159
|
+
});
|
|
160
|
+
const playbackPlanner = createPlaybackPlanner({
|
|
161
|
+
ffmpegBin,
|
|
162
|
+
transcodeAudioEnabled: transcodeAudio,
|
|
163
|
+
localBaseUrl: hlsSessionManager.localBaseUrl,
|
|
164
|
+
sourceRegistry,
|
|
165
|
+
torrentPool
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
app.get("/health", async (req, reply) => handleHealthGet(req, reply, { version }));
|
|
169
|
+
app.get("/healthz", async (req, reply) => handleHealthzGet(req, reply, { version }));
|
|
170
|
+
app.post("/api/sources", async (req, reply) =>
|
|
171
|
+
handleApiSourcesPost(req, reply, { sourceRegistry })
|
|
172
|
+
);
|
|
173
|
+
app.get("/api/sources/:sourceKey/stats", async (req, reply) =>
|
|
174
|
+
handleApiSourceStatsGet(req, reply, { sourceRegistry, torrentPool })
|
|
175
|
+
);
|
|
176
|
+
app.get("/api/sources/:sourceKey/files", async (req, reply) =>
|
|
177
|
+
handleApiSourceFilesGet(req, reply, { sourceRegistry, torrentPool })
|
|
178
|
+
);
|
|
179
|
+
app.post("/api/playback-plan", async (req, reply) =>
|
|
180
|
+
handleApiPlaybackPlanPost(req, reply, { playbackPlanner })
|
|
181
|
+
);
|
|
182
|
+
app.get("/api/subtitles", async (req, reply) =>
|
|
183
|
+
handleApiSubtitlesGet(req, reply, {
|
|
184
|
+
sourceRegistry,
|
|
185
|
+
torrentPool,
|
|
186
|
+
ffmpegBin,
|
|
187
|
+
localBaseUrl: hlsSessionManager.localBaseUrl
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
app.get("/stream", async (req, reply) =>
|
|
191
|
+
handleStreamGet(req, reply, { sourceRegistry, torrentPool })
|
|
192
|
+
);
|
|
193
|
+
app.post("/api/transcode-sessions", async (req, reply) =>
|
|
194
|
+
handleApiTranscodeSessionsPost(req, reply, { hlsSessionManager })
|
|
195
|
+
);
|
|
196
|
+
app.post("/api/transcode-sessions/:sessionId/release", async (req, reply) =>
|
|
197
|
+
handleApiTranscodeSessionReleasePost(req, reply, { hlsSessionManager })
|
|
198
|
+
);
|
|
199
|
+
app.get("/api/transcode-sessions/:sessionId/progress", async (req, reply) =>
|
|
200
|
+
handleApiTranscodeSessionsProgressGet(req, reply, { hlsSessionManager })
|
|
201
|
+
);
|
|
202
|
+
app.post("/api/transcode-sessions/:sessionId/net-report", async (req, reply) =>
|
|
203
|
+
handleApiTranscodeSessionNetReportPost(req, reply, { hlsSessionManager })
|
|
204
|
+
);
|
|
205
|
+
app.post("/api/transcode-sessions/:sessionId/seek", async (req, reply) =>
|
|
206
|
+
handleApiTranscodeSessionSeekPost(req, reply, { hlsSessionManager })
|
|
207
|
+
);
|
|
208
|
+
app.get("/transcode/:sessionId/:fileName", async (req, reply) =>
|
|
209
|
+
handleTranscodeSessionFileGet(req, reply, { hlsSessionManager })
|
|
210
|
+
);
|
|
211
|
+
await app.register(fastifyStatic, {
|
|
212
|
+
root: publicRoot,
|
|
213
|
+
prefix: "/",
|
|
214
|
+
serveDotFiles: true
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
app.addHook("onClose", async () => {
|
|
218
|
+
// Order matters: stop the ffmpeg readers (HLS sessions) before destroying
|
|
219
|
+
// the torrents whose files they read from, then remove the torrent data.
|
|
220
|
+
await hlsSessionManager.disposeAll();
|
|
221
|
+
await torrentPool.destroyAll();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await app.listen({ host, port: selectedPort });
|
|
225
|
+
return {
|
|
226
|
+
app,
|
|
227
|
+
port: selectedPort
|
|
228
|
+
};
|
|
229
|
+
}
|