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 { import {
deleteTempFile, deleteTempFile,
deleteTempFileIgnoringErrors, deleteTempFileIgnoringErrors,
makeFileForDataOrStreamOrPathOrZipItem,
makeTempFilePath, makeTempFilePath,
} from "./utils/temp"; } from "./utils/temp";
@ -80,10 +81,7 @@ const handleStreamRequest = async (request: Request): Promise<Response> => {
case "convert-to-mp4": case "convert-to-mp4":
return handleConvertToMP4Write(request); return handleConvertToMP4Write(request);
case "generate-hls": case "generate-hls":
return handleGenerateHLSWrite( return handleGenerateHLSWrite(request, searchParams);
request,
searchParams.get("objectUploadURL")!,
);
default: default:
return new Response(`Unknown op ${op}`, { return new Response(`Unknown op ${op}`, {
status: 404, 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 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 * the HLS playlist itself, and one for the file containing the encrypted and
* transcoded video chunks. The video stream we write to the provided * transcoded video chunks. The video stream we write to the objectUploadURL
* {@link objectUploadURL}, and then return a JSON object containing the token * (provided via {@link params}), and then we return a JSON object containing
* for the playlist, and other metadata for use by the renderer. * the token for the playlist, and other metadata for use by the renderer.
*/ */
const handleGenerateHLSWrite = async ( const handleGenerateHLSWrite = async (
request: Request, request: Request,
objectUploadURL: string, params: URLSearchParams,
) => { ) => {
const inputTempFilePath = await makeTempFilePath(); const objectUploadURL = params.get("objectUploadURL");
await writeStream(inputTempFilePath, request.body!); 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(); const outputFilePathPrefix = await makeTempFilePath();
let result: FFmpegGenerateHLSPlaylistAndSegmentsResult; let result: FFmpegGenerateHLSPlaylistAndSegmentsResult;
try { try {
await writeToTemporaryInputFile();
result = await ffmpegGenerateHLSPlaylistAndSegments( result = await ffmpegGenerateHLSPlaylistAndSegments(
inputTempFilePath, inputFilePath,
outputFilePathPrefix, outputFilePathPrefix,
); );
@ -319,7 +341,8 @@ const handleGenerateHLSWrite = async (
await deleteTempFileIgnoringErrors(videoPath); await deleteTempFileIgnoringErrors(videoPath);
} }
} finally { } 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 type { ZipItem } from "../../types/ipc";
import log from "../log"; import log from "../log";
import { markClosableZip, openZip } from "../services/zip"; import { markClosableZip, openZip } from "../services/zip";
import { writeStream } from "./stream";
/** /**
* Our very own directory within the system temp directory. Go crazy, but * 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. * a zip file, name of an entry within that zip file) tuple.
*/ */
export const makeFileForDataOrStreamOrPathOrZipItem = async ( export const makeFileForDataOrStreamOrPathOrZipItem = async (
item: Uint8Array | string | ZipItem, item: Uint8Array | ReadableStream | string | ZipItem,
): Promise<FileForDataOrPathOrZipItem> => { ): Promise<FileForDataOrPathOrZipItem> => {
let path: string; let path: string;
let isFileTemporary: boolean; let isFileTemporary: boolean;
@ -127,6 +128,8 @@ export const makeFileForDataOrStreamOrPathOrZipItem = async (
isFileTemporary = true; isFileTemporary = true;
if (item instanceof Uint8Array) { if (item instanceof Uint8Array) {
writeToTemporaryFile = () => fs.writeFile(path, item); writeToTemporaryFile = () => fs.writeFile(path, item);
} else if (item instanceof ReadableStream) {
writeToTemporaryFile = () => writeStream(path, item);
} else { } else {
writeToTemporaryFile = async () => { writeToTemporaryFile = async () => {
const [zipPath, entryName] = item; const [zipPath, entryName] = item;