Implement node side

This commit is contained in:
Manav Rathi 2025-04-25 18:47:00 +05:30
parent d95864be1c
commit 6b8800f151
No known key found for this signature in database
2 changed files with 39 additions and 13 deletions

View File

@ -19,6 +19,7 @@ import { writeStream } from "./utils/stream";
import {
deleteTempFile,
deleteTempFileIgnoringErrors,
makeFileForDataOrStreamOrPathOrZipItem,
makeTempFilePath,
} from "./utils/temp";
@ -80,10 +81,7 @@ const handleStreamRequest = async (request: Request): Promise<Response> => {
case "convert-to-mp4":
return handleConvertToMP4Write(request);
case "generate-hls":
return handleGenerateHLSWrite(
request,
searchParams.get("objectUploadURL")!,
);
return handleGenerateHLSWrite(request, searchParams);
default:
return new Response(`Unknown op ${op}`, {
status: 404,
@ -281,22 +279,46 @@ const handleVideoDone = async (token: string) => {
*
* The difference here is that we the conversion generates two streams - one for
* the HLS playlist itself, and one for the file containing the encrypted and
* transcoded video chunks. The video stream we write to the provided
* {@link objectUploadURL}, and then return a JSON object containing the token
* for the playlist, and other metadata for use by the renderer.
* transcoded video chunks. The video stream we write to the objectUploadURL
* (provided via {@link params}), and then we return a JSON object containing
* the token for the playlist, and other metadata for use by the renderer.
*/
const handleGenerateHLSWrite = async (
request: Request,
objectUploadURL: string,
params: URLSearchParams,
) => {
const inputTempFilePath = await makeTempFilePath();
await writeStream(inputTempFilePath, request.body!);
const objectUploadURL = params.get("objectUploadURL");
if (!objectUploadURL) throw new Error("Missing objectUploadURL");
let inputItem: Parameters<typeof makeFileForDataOrStreamOrPathOrZipItem>[0];
const path = params.get("path");
if (path) {
inputItem = path;
} else {
const zipPath = params.get("zipPath");
const entryName = params.get("entryName");
if (zipPath && entryName) {
inputItem = [zipPath, entryName];
} else {
const body = request.body;
if (!body) throw new Error("Missing body");
inputItem = body;
}
}
const {
path: inputFilePath,
isFileTemporary: isInputFileTemporary,
writeToTemporaryFile: writeToTemporaryInputFile,
} = await makeFileForDataOrStreamOrPathOrZipItem(inputItem);
const outputFilePathPrefix = await makeTempFilePath();
let result: FFmpegGenerateHLSPlaylistAndSegmentsResult;
try {
await writeToTemporaryInputFile();
result = await ffmpegGenerateHLSPlaylistAndSegments(
inputTempFilePath,
inputFilePath,
outputFilePathPrefix,
);
@ -319,7 +341,8 @@ const handleGenerateHLSWrite = async (
await deleteTempFileIgnoringErrors(videoPath);
}
} finally {
await deleteTempFileIgnoringErrors(inputTempFilePath);
if (isInputFileTemporary)
await deleteTempFileIgnoringErrors(inputFilePath);
}
};

View File

@ -5,6 +5,7 @@ import path from "node:path";
import type { ZipItem } from "../../types/ipc";
import log from "../log";
import { markClosableZip, openZip } from "../services/zip";
import { writeStream } from "./stream";
/**
* Our very own directory within the system temp directory. Go crazy, but
@ -111,7 +112,7 @@ interface FileForDataOrPathOrZipItem {
* a zip file, name of an entry within that zip file) tuple.
*/
export const makeFileForDataOrStreamOrPathOrZipItem = async (
item: Uint8Array | string | ZipItem,
item: Uint8Array | ReadableStream | string | ZipItem,
): Promise<FileForDataOrPathOrZipItem> => {
let path: string;
let isFileTemporary: boolean;
@ -127,6 +128,8 @@ export const makeFileForDataOrStreamOrPathOrZipItem = async (
isFileTemporary = true;
if (item instanceof Uint8Array) {
writeToTemporaryFile = () => fs.writeFile(path, item);
} else if (item instanceof ReadableStream) {
writeToTemporaryFile = () => writeStream(path, item);
} else {
writeToTemporaryFile = async () => {
const [zipPath, entryName] = item;