mes-engine
Version:
A powerful and flexible video processing framework for Node.js with support for multiple processing engines, adaptive streaming, and intelligent caching.
90 lines (76 loc) • 2.97 kB
text/typescript
// processor.ts
import { VideoEngine } from './core/VideoEngine';
import { EventEmitter } from 'events';
import { StorageProvider } from './storage/StorageProvider';
import { StreamManager } from './streaming/StreamManager';
import { VideoConfig, VideoManifest } from './core/types';
import { join } from 'path';
import { promises as fs } from 'fs';
import { Readable } from 'stream';
import { VideoEvent } from './core/events';
export class VideoProcessor extends EventEmitter {
private engine: VideoEngine;
private streamManager: StreamManager;
private config: VideoConfig;
constructor(
engine: VideoEngine,
storage: StorageProvider,
config: VideoConfig
) {
super();
this.engine = engine;
this.streamManager = new StreamManager(storage);
this.config = config;
}
async processVideo(inputPath: string): Promise<VideoManifest> {
const videoId = await this.generateVideoId(inputPath);
const manifest: VideoManifest = {
videoId,
qualities: this.config.defaultQualities,
chunks: []
};
const duration = await this.engine.getDuration(inputPath);
const chunks = Math.ceil(duration / this.config.chunkSize);
for (const quality of this.config.defaultQualities) {
for (let i = 0; i < chunks; i++) {
const chunkPath = this.getChunkPath(videoId, quality.height, i);
try {
await this.engine.processChunk(
inputPath,
chunkPath,
i * this.config.chunkSize,
quality
);
manifest.chunks.push({
quality: quality.height,
number: i,
path: chunkPath
});
this.emit(VideoEvent.CHUNK_PROCESSED, { quality, chunkNumber: i });
} catch (error) {
this.emit(VideoEvent.ERROR, error);
throw error;
}
}
this.emit(VideoEvent.QUALITY_PROCESSED, quality);
}
this.emit(VideoEvent.PROCESSING_COMPLETE, manifest);
return manifest;
}
async streamChunk(
videoId: string,
quality: number,
chunkNumber: number,
range?: { start: number; end: number }
): Promise<Readable> {
const chunkPath = this.getChunkPath(videoId, quality, chunkNumber);
return this.streamManager.createStream(chunkPath, range);
}
private getChunkPath(videoId: string, quality: number, chunkNumber: number): string {
return join(this.config.cacheDir, videoId, `${quality}p`, `chunk_${chunkNumber}.mp4`);
}
private async generateVideoId(inputPath: string): Promise<string> {
const stats = await fs.stat(inputPath);
return `${inputPath.split('/').pop()?.split('.')[0]}_${stats.mtimeMs}`;
}
}