api/stream: split types.js into proxy.js and ffmpeg.js

This commit is contained in:
wukko 2025-06-24 20:09:41 +06:00
parent aa376d76f6
commit 14657e51d3
No known key found for this signature in database
GPG Key ID: 3E30B3F26C7B4AA2
3 changed files with 50 additions and 46 deletions

View File

@ -1,12 +1,11 @@
import ffmpeg from "ffmpeg-static"; import ffmpeg from "ffmpeg-static";
import { spawn } from "child_process"; import { spawn } from "child_process";
import { Agent, request } from "undici";
import { create as contentDisposition } from "content-disposition-header"; import { create as contentDisposition } from "content-disposition-header";
import { env } from "../config.js"; import { env } from "../config.js";
import { destroyInternalStream } from "./manage.js"; import { destroyInternalStream } from "./manage.js";
import { hlsExceptions } from "../processing/service-config.js"; import { hlsExceptions } from "../processing/service-config.js";
import { getHeaders, closeRequest, closeResponse, pipe, estimateTunnelLength, estimateAudioMultiplier } from "./shared.js"; import { closeResponse, pipe, estimateTunnelLength, estimateAudioMultiplier } from "./shared.js";
const metadataTags = [ const metadataTags = [
"album", "album",
@ -55,44 +54,6 @@ const getCommand = (args) => {
return [ffmpeg, args] return [ffmpeg, args]
} }
const defaultAgent = new Agent();
const proxy = async (streamInfo, res) => {
const abortController = new AbortController();
const shutdown = () => (
closeRequest(abortController),
closeResponse(res),
destroyInternalStream(streamInfo.urls)
);
try {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
res.setHeader('Content-disposition', contentDisposition(streamInfo.filename));
const { body: stream, headers, statusCode } = await request(streamInfo.urls, {
headers: {
...getHeaders(streamInfo.service),
Range: streamInfo.range
},
signal: abortController.signal,
maxRedirections: 16,
dispatcher: defaultAgent,
});
res.status(statusCode);
for (const headerName of ['accept-ranges', 'content-type', 'content-length']) {
if (headers[headerName]) {
res.setHeader(headerName, headers[headerName]);
}
}
pipe(stream, res, shutdown);
} catch {
shutdown();
}
}
const render = async (res, streamInfo, ffargs, multiplier) => { const render = async (res, streamInfo, ffargs, multiplier) => {
let process; let process;
const urls = Array.isArray(streamInfo.urls) ? streamInfo.urls : [streamInfo.urls]; const urls = Array.isArray(streamInfo.urls) ? streamInfo.urls : [streamInfo.urls];
@ -245,7 +206,6 @@ const convertGif = async (streamInfo, res) => {
} }
export default { export default {
proxy,
remux, remux,
convertAudio, convertAudio,
convertGif, convertGif,

43
api/src/stream/proxy.js Normal file
View File

@ -0,0 +1,43 @@
import { Agent, request } from "undici";
import { create as contentDisposition } from "content-disposition-header";
import { destroyInternalStream } from "./manage.js";
import { getHeaders, closeRequest, closeResponse, pipe } from "./shared.js";
const defaultAgent = new Agent();
export default async function (streamInfo, res) {
const abortController = new AbortController();
const shutdown = () => (
closeRequest(abortController),
closeResponse(res),
destroyInternalStream(streamInfo.urls)
);
try {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
res.setHeader('Content-disposition', contentDisposition(streamInfo.filename));
const { body: stream, headers, statusCode } = await request(streamInfo.urls, {
headers: {
...getHeaders(streamInfo.service),
Range: streamInfo.range
},
signal: abortController.signal,
maxRedirections: 16,
dispatcher: defaultAgent,
});
res.status(statusCode);
for (const headerName of ['accept-ranges', 'content-type', 'content-length']) {
if (headers[headerName]) {
res.setHeader(headerName, headers[headerName]);
}
}
pipe(stream, res, shutdown);
} catch {
shutdown();
}
}

View File

@ -1,4 +1,5 @@
import stream from "./types.js"; import proxy from "./proxy.js";
import ffmpeg from "./ffmpeg.js";
import { closeResponse } from "./shared.js"; import { closeResponse } from "./shared.js";
import { internalStream } from "./internal.js"; import { internalStream } from "./internal.js";
@ -7,7 +8,7 @@ export default async function(res, streamInfo) {
try { try {
switch (streamInfo.type) { switch (streamInfo.type) {
case "proxy": case "proxy":
return await stream.proxy(streamInfo, res); return await proxy(streamInfo, res);
case "internal": case "internal":
return await internalStream(streamInfo.data, res); return await internalStream(streamInfo.data, res);
@ -15,13 +16,13 @@ export default async function(res, streamInfo) {
case "merge": case "merge":
case "remux": case "remux":
case "mute": case "mute":
return await stream.remux(streamInfo, res); return await ffmpeg.remux(streamInfo, res);
case "audio": case "audio":
return await stream.convertAudio(streamInfo, res); return await ffmpeg.convertAudio(streamInfo, res);
case "gif": case "gif":
return await stream.convertGif(streamInfo, res); return await ffmpeg.convertGif(streamInfo, res);
} }
closeResponse(res); closeResponse(res);