UNPKG

@eleven-am/transcoder

Version:

High-performance HLS transcoding library with hardware acceleration, intelligent client management, and distributed processing support for Node.js

205 lines 7.97 kB
"use strict"; /* * @eleven-am/transcoder * Copyright (C) 2025 Roy OSSAI * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.FileStorage = void 0; const crypto = __importStar(require("crypto")); const fs = __importStar(require("fs")); const pfs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const fp_1 = require("@eleven-am/fp"); class FileStorage { constructor(cacheDirectory) { this.cacheDirectory = cacheDirectory; fs.mkdirSync(this.cacheDirectory, { recursive: true }); } /** * Generates a unique file ID based on the file's inode number. * @param filePath The path to the file. * @returns A TaskEither containing the generated file ID or an error. */ generateFileId(filePath) { return fp_1.TaskEither .tryCatch(() => pfs.stat(filePath), 'Failed to generate file ID') .map((stat) => { const hash = crypto.createHash('sha256'); hash.update(stat.ino.toString()); return hash.digest('hex'); }); } /** * Deletes a file at the specified path. * @param path The path to the file to delete. * @returns A TaskEither indicating success or failure. */ deleteFile(path) { return fp_1.TaskEither .tryCatch(() => pfs.unlink(path), 'Failed to delete file') .orElse((err) => { if (err.error.message.includes('ENOENT')) { return fp_1.TaskEither.of(undefined); } return fp_1.TaskEither.error((0, fp_1.createUnknownError)('Failed to delete file')(err.error)); }); } /** * Deletes all files with a specified prefix. * @param prefix The prefix to match files against. * @returns A TaskEither indicating success or failure. */ deleteFilesWithPrefix(prefix) { return this.listFiles(prefix) .chainItems((file) => this.deleteFile(file)) .map(() => undefined); } /** * Checks if a file exists at the specified path. * @param path The path to check. * @returns A TaskEither containing true if the file exists, false otherwise. */ exists(path) { return fp_1.TaskEither .tryCatch(() => pfs.stat(path), 'Failed to check file existence') .map((stat) => stat.isFile()) .orElse(() => fp_1.TaskEither.of(false)); } /** * Gets the base path for a given file ID. * @param fileId The file ID. * @returns The base path for the file ID. */ getBasePath(fileId) { return path.join(this.cacheDirectory, fileId); } /** * Gets a media source for a file at the specified path. * @param filePath The path to the file. * @returns A MediaSource object for the file. */ getFileStream(filePath) { return fp_1.TaskEither .tryCatch(() => pfs.access(filePath, fs.constants.F_OK), 'Failed to create read stream') .map(() => fs.createReadStream(filePath)); } /** * Lists all files that begin with the given prefix. * @param prefix The path prefix to search for. */ listFiles(prefix) { const itemStats = (item) => fp_1.TaskEither .tryCatch(() => pfs.stat(item), 'Failed to get file stats') .map((stat) => ({ path: item, isDirectory: stat.isDirectory(), })) .matchTask([ { predicate: (item) => item.isDirectory, // eslint-disable-next-line @typescript-eslint/no-use-before-define run: (item) => readDir(item.path), }, { predicate: (item) => !item.isDirectory, run: (item) => fp_1.TaskEither.of([item.path]), }, ]); const readDir = (dir) => fp_1.TaskEither .tryCatch(() => pfs.readdir(dir), 'Failed to read directory') .chainItems(itemStats) .map((items) => items.flat()); return fp_1.TaskEither .tryCatch(() => pfs.readdir(prefix), 'Failed to read directory') .chainItems(itemStats) .map((items) => items.flat()); } /** * Saves data from a readable stream to storage. * @param filePath The destination path. * @param content The readable stream containing the data to save. */ saveFile(filePath, content) { const promise = new Promise((resolve, reject) => { const writeStream = fs.createWriteStream(filePath); content.on('error', reject); writeStream.on('finish', resolve); writeStream.on('error', reject); content.pipe(writeStream); }); return fp_1.TaskEither .of(fs.mkdirSync(path.dirname(filePath), { recursive: true })) .chain(() => fp_1.TaskEither .tryCatch(() => promise, 'Failed to save file')); } /** * Gets the size of a file in bytes. * @param path The path to the file. * @returns A TaskEither containing the file size or an error. */ getFileSize(path) { return this.exists(path) .filter((exists) => exists, () => (0, fp_1.createNotFoundError)(`File not found: ${path}`)) .chain(() => fp_1.TaskEither .tryCatch(() => pfs.stat(path), 'Failed to get file size') .map((stat) => stat.size)); } /** * Ensures that the directory for a given path exists. * @param path The path of the directory to check. * @returns A TaskEither containing the path if successful, or an error. */ ensureDirectoryExists(path) { return fp_1.TaskEither .tryCatch(() => pfs.mkdir(path, { recursive: true }), 'Failed to create directory') .map(() => path); } } exports.FileStorage = FileStorage; //# sourceMappingURL=fileStorage.js.map