3 Commits
v1.1.0 ... main

Author SHA1 Message Date
9a13b20164 v1.1.1 2025-12-11 23:07:36 +00:00
95cf2a6a3a fix(smartffmpeg): Bump patch version to 1.1.1 2025-12-11 23:07:36 +00:00
329e6ebef5 update 2025-12-11 23:06:21 +00:00
16 changed files with 9 additions and 1984 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.nogit/
node_modules/
dist_*

View File

@@ -1,5 +1,10 @@
# Changelog
## 2025-12-11 - 1.1.1 - fix(smartffmpeg)
Bump patch version to 1.1.1
- No source changes detected in the working tree — prepare a patch release (1.1.1) for packaging/release purposes.
## 2025-12-11 - 1.1.0 - feat(core)
Initial release: add SmartFfmpeg fluent builder API with in-memory streams, progress tracking, and bundled ffmpeg/ffprobe

View File

@@ -1,8 +0,0 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export declare const commitinfo: {
name: string;
version: string;
description: string;
};

View File

@@ -1,9 +0,0 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/smartffmpeg',
version: '1.0.0',
description: 'A fast Node.js module for media file conversion using ffmpeg'
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDhEQUE4RDtDQUM1RSxDQUFBIn0=

View File

@@ -1,288 +0,0 @@
import type * as interfaces from './interfaces.js';
/**
* Input source for FfmpegCommand
* Supports file paths, memory buffers, and Web Streams
*/
export type TFfmpegInput = string | Buffer | Uint8Array | ReadableStream<Uint8Array>;
/**
* Output destination for FfmpegCommand
*/
export type TFfmpegOutput = string | 'buffer' | 'stream' | WritableStream<Uint8Array>;
/**
* Input options when using Buffer or Stream
*/
export interface IInputOptions {
/** Input format (required for buffer/stream input) */
format?: interfaces.TOutputFormat;
/** Input duration hint for progress calculation */
duration?: number;
}
/**
* Result of running an FfmpegCommand
*/
export interface IFfmpegResult {
/** Output buffer (when outputting to buffer) */
buffer?: Buffer;
/** Output stream (when outputting to stream) */
stream?: ReadableStream<Uint8Array>;
/** Path to output file (when outputting to file) */
outputPath?: string;
/** Duration of processing in ms */
processingTime: number;
}
/**
* Progress callback type for FfmpegCommand
*/
export type TFfmpegProgressCallback = (progress: interfaces.IProgressInfo) => void;
/**
* Modern fluent builder API for ffmpeg commands
*
* @example
* ```typescript
* // File to file
* await ffmpeg.create()
* .input('/path/to/input.mp4')
* .videoCodec('libx264')
* .audioBitrate('128k')
* .output('/path/to/output.mp4')
* .run();
*
* // Buffer to buffer
* const outputBuffer = await ffmpeg.create()
* .input(inputBuffer, { format: 'mp4' })
* .videoCodec('libx264')
* .toBuffer('webm');
*
* // With progress
* await ffmpeg.create()
* .input('/path/to/input.mp4')
* .videoCodec('libx264')
* .onProgress(p => console.log(`${p.percent}%`))
* .output('/path/to/output.mp4')
* .run();
* ```
*/
export declare class FfmpegCommand {
private ffmpegPath;
private ffprobePath;
private inputSource;
private inputOpts;
private customInputArgs;
private outputDest;
private outputFormat;
private customOutputArgs;
private _videoCodec;
private _videoBitrate;
private _width;
private _height;
private _fps;
private _crf;
private _preset;
private _noVideo;
private _audioCodec;
private _audioBitrate;
private _sampleRate;
private _audioChannels;
private _noAudio;
private _startTime;
private _duration;
private videoFilters;
private audioFilters;
private _complexFilter;
private extraInputArgs;
private extraOutputArgs;
private progressCallback;
private _overwrite;
constructor(ffmpegPath: string, ffprobePath: string);
/**
* Set the input source
* @param source - File path, Buffer, or Readable stream
* @param options - Input options (format required for buffer/stream)
*/
input(source: TFfmpegInput, options?: IInputOptions): this;
/**
* Seek to position before input (fast seek)
* @param time - Time in seconds or timecode string
*/
seekInput(time: number | string): this;
/**
* Add custom input arguments
*/
inputArgs(...args: string[]): this;
/**
* Set video codec
*/
videoCodec(codec: interfaces.TVideoCodec): this;
/**
* Set video bitrate
* @param bitrate - e.g., '1M', '2000k'
*/
videoBitrate(bitrate: string): this;
/**
* Set output dimensions
* @param width - Width in pixels (-1 for auto)
* @param height - Height in pixels (-1 for auto)
*/
size(width: number, height?: number): this;
/**
* Set frame rate
*/
fps(rate: number): this;
/**
* Set Constant Rate Factor (quality)
* @param value - 0-51, lower is better quality
*/
crf(value: number): this;
/**
* Set encoding preset
*/
preset(value: interfaces.TPreset): this;
/**
* Remove video stream
*/
noVideo(): this;
/**
* Add a video filter
* @param filter - Filter string (e.g., 'scale=1920:1080', 'hflip')
*/
videoFilter(filter: string): this;
/**
* Scale video
* @param width - Width (-1 or -2 for auto)
* @param height - Height (-1 or -2 for auto)
*/
scale(width: number | string, height: number | string): this;
/**
* Crop video
*/
crop(width: number, height: number, x?: number, y?: number): this;
/**
* Rotate video
* @param angle - Rotation in radians or 'PI/2', 'PI', etc.
*/
rotate(angle: number | string): this;
/**
* Flip video horizontally
*/
flipHorizontal(): this;
/**
* Flip video vertically
*/
flipVertical(): this;
/**
* Add padding to video
*/
pad(width: number, height: number, x?: number, y?: number, color?: string): this;
/**
* Set audio codec
*/
audioCodec(codec: interfaces.TAudioCodec): this;
/**
* Set audio bitrate
* @param bitrate - e.g., '128k', '320k'
*/
audioBitrate(bitrate: string): this;
/**
* Set audio sample rate
* @param rate - Sample rate in Hz (e.g., 44100, 48000)
*/
sampleRate(rate: number): this;
/**
* Set number of audio channels
* @param channels - 1 for mono, 2 for stereo
*/
audioChannels(channels: number): this;
/**
* Remove audio stream
*/
noAudio(): this;
/**
* Add an audio filter
* @param filter - Filter string (e.g., 'volume=2', 'aecho=0.8:0.88:60:0.4')
*/
audioFilter(filter: string): this;
/**
* Set audio volume
* @param level - Volume multiplier (e.g., 2 for double, 0.5 for half)
*/
volume(level: number): this;
/**
* Normalize audio
*/
normalize(): this;
/**
* Set start time (seek)
* @param time - Time in seconds or timecode string
*/
seek(time: number | string): this;
/**
* Set output duration
* @param time - Duration in seconds or timecode string
*/
duration(time: number | string): this;
/**
* Set both start and end time
*/
trim(start: number | string, end: number | string): this;
/**
* Set a complex filter graph
* @param filterGraph - Complex filter string
*/
complexFilter(filterGraph: string): this;
/**
* Set output format
*/
format(fmt: interfaces.TOutputFormat): this;
/**
* Set output destination (file path)
*/
output(dest: string): this;
/**
* Add custom output arguments
*/
outputArgs(...args: string[]): this;
/**
* Set overwrite flag
*/
overwrite(value?: boolean): this;
/**
* Set progress callback
*/
onProgress(callback: TFfmpegProgressCallback): this;
/**
* Run the command and output to file
*/
run(): Promise<IFfmpegResult>;
/**
* Run the command and return output as Buffer
* @param format - Output format
*/
toBuffer(format?: interfaces.TOutputFormat): Promise<Buffer>;
/**
* Run the command and return output as Web ReadableStream
* @param format - Output format
*/
toStream(format?: interfaces.TOutputFormat): ReadableStream<Uint8Array>;
/**
* Pipe output to a Web WritableStream
* @param writable - Web WritableStream
* @param format - Output format
*/
pipe(writable: WritableStream<Uint8Array>, format?: interfaces.TOutputFormat): Promise<void>;
/**
* Get the ffmpeg arguments that would be used (for debugging)
*/
getArgs(outputPath?: string): string[];
private buildArgs;
private execute;
private executeToBuffer;
private executeToNodeStream;
private parseProgress;
private getInputDuration;
private runProbe;
private formatTime;
private isNodeStream;
private isWebReadableStream;
private isMemoryInput;
private pipeInputToProcess;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,185 +0,0 @@
import type * as interfaces from './interfaces.js';
import { FfmpegCommand, type TFfmpegInput, type IInputOptions } from './classes.ffmpegcommand.js';
/**
* Event callback types
*/
export type TProgressCallback = (progress: interfaces.IProgressInfo) => void;
export type TErrorCallback = (error: Error) => void;
export type TEndCallback = () => void;
/**
* SmartFfmpeg - A fast Node.js module for media file conversion using ffmpeg
*
* @example Modern Builder API
* ```typescript
* const ffmpeg = new SmartFfmpeg();
*
* // File to file with fluent API
* await ffmpeg.create()
* .input('/path/to/input.mp4')
* .videoCodec('libx264')
* .audioBitrate('128k')
* .size(1920, 1080)
* .output('/path/to/output.mp4')
* .run();
*
* // Buffer to buffer
* const outputBuffer = await ffmpeg.create()
* .input(inputBuffer, { format: 'mp4' })
* .videoCodec('libx264')
* .toBuffer('webm');
*
* // With progress tracking
* await ffmpeg.create()
* .input('/path/to/input.mp4')
* .videoCodec('libx264')
* .onProgress(p => console.log(`${p.percent?.toFixed(1)}%`))
* .output('/path/to/output.mp4')
* .run();
*
* // Stream output
* const stream = ffmpeg.create()
* .input('/path/to/input.mp4')
* .videoCodec('libx264')
* .toStream('mp4');
* ```
*
* @example Legacy API (still supported)
* ```typescript
* await ffmpeg.convert('input.mp4', 'output.webm', {
* videoCodec: 'libvpx-vp9',
* audioBitrate: '128k'
* });
* ```
*/
export declare class SmartFfmpeg {
private ffmpegPath;
private ffprobePath;
constructor();
/**
* Create a new FfmpegCommand builder for fluent API usage
*
* @example
* ```typescript
* await ffmpeg.create()
* .input('/path/to/input.mp4')
* .videoCodec('libx264')
* .crf(23)
* .output('/path/to/output.mp4')
* .run();
* ```
*/
create(): FfmpegCommand;
/**
* Shorthand to create a command with input already set
*
* @example
* ```typescript
* // File input
* await ffmpeg.input('/path/to/input.mp4')
* .videoCodec('libx264')
* .output('/path/to/output.mp4')
* .run();
*
* // Buffer input
* const output = await ffmpeg.input(buffer, { format: 'mp4' })
* .videoCodec('libx264')
* .toBuffer('webm');
* ```
*/
input(source: TFfmpegInput, options?: IInputOptions): FfmpegCommand;
/**
* Get media file information using ffprobe
*/
getMediaInfo(inputPath: string): Promise<interfaces.IMediaInfo>;
/**
* Convert media file with specified options
*/
convert(inputPath: string, outputPath: string, options?: interfaces.IConversionOptions): Promise<void>;
/**
* Convert media file with progress reporting
*/
convertWithProgress(inputPath: string, outputPath: string, options?: interfaces.IConversionOptions, onProgress?: TProgressCallback): Promise<void>;
/**
* Extract audio from video file
*/
extractAudio(inputPath: string, outputPath: string, options?: Pick<interfaces.IConversionOptions, 'audioCodec' | 'audioBitrate' | 'sampleRate' | 'audioChannels' | 'startTime' | 'duration' | 'overwrite'>): Promise<void>;
/**
* Remove audio from video file
*/
removeAudio(inputPath: string, outputPath: string, options?: Pick<interfaces.IConversionOptions, 'videoCodec' | 'videoBitrate' | 'overwrite'>): Promise<void>;
/**
* Take a screenshot at a specific time
*/
screenshot(inputPath: string, outputPath: string, options: interfaces.IScreenshotOptions): Promise<void>;
/**
* Generate multiple thumbnails from video
*/
generateThumbnails(inputPath: string, outputDir: string, options: interfaces.IThumbnailOptions): Promise<string[]>;
/**
* Resize video
*/
resize(inputPath: string, outputPath: string, width?: number, height?: number, options?: Omit<interfaces.IConversionOptions, 'width' | 'height'>): Promise<void>;
/**
* Change video frame rate
*/
changeFrameRate(inputPath: string, outputPath: string, fps: number, options?: Omit<interfaces.IConversionOptions, 'fps'>): Promise<void>;
/**
* Trim media file
*/
trim(inputPath: string, outputPath: string, startTime: number | string, duration: number | string, options?: Omit<interfaces.IConversionOptions, 'startTime' | 'duration'>): Promise<void>;
/**
* Convert to GIF
*/
toGif(inputPath: string, outputPath: string, options?: {
width?: number;
height?: number;
fps?: number;
startTime?: number | string;
duration?: number | string;
}): Promise<void>;
/**
* Concatenate multiple media files
*/
concat(inputPaths: string[], outputPath: string, options?: interfaces.IConversionOptions): Promise<void>;
/**
* Add audio to video
*/
addAudio(videoPath: string, audioPath: string, outputPath: string, options?: {
videoCodec?: interfaces.TVideoCodec;
audioCodec?: interfaces.TAudioCodec;
audioBitrate?: string;
shortest?: boolean;
overwrite?: boolean;
}): Promise<void>;
/**
* Get available encoders
*/
getEncoders(): Promise<string[]>;
/**
* Get available decoders
*/
getDecoders(): Promise<string[]>;
/**
* Get available formats
*/
getFormats(): Promise<string[]>;
/**
* Run ffmpeg with raw arguments
*/
runRaw(args: string[]): Promise<{
stdout: string;
stderr: string;
}>;
/**
* Run ffprobe with raw arguments
*/
runProbeRaw(args: string[]): Promise<{
stdout: string;
stderr: string;
}>;
private buildConversionArgs;
private buildScaleFilter;
private formatTime;
private parseStreamInfo;
private runProcess;
}

File diff suppressed because one or more lines are too long

3
dist_ts/index.d.ts vendored
View File

@@ -1,3 +0,0 @@
export * from './classes.smartffmpeg.js';
export * from './classes.ffmpegcommand.js';
export * from './interfaces.js';

View File

@@ -1,7 +0,0 @@
// Export main class
export * from './classes.smartffmpeg.js';
// Export builder command class
export * from './classes.ffmpegcommand.js';
// Export interfaces and types
export * from './interfaces.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvQkFBb0I7QUFDcEIsY0FBYywwQkFBMEIsQ0FBQztBQUV6QywrQkFBK0I7QUFDL0IsY0FBYyw0QkFBNEIsQ0FBQztBQUUzQyw4QkFBOEI7QUFDOUIsY0FBYyxpQkFBaUIsQ0FBQyJ9

View File

@@ -1,138 +0,0 @@
/**
* Video codec options
*/
export type TVideoCodec = 'libx264' | 'libx265' | 'libvpx' | 'libvpx-vp9' | 'libaom-av1' | 'mpeg4' | 'copy' | string;
/**
* Audio codec options
*/
export type TAudioCodec = 'aac' | 'libmp3lame' | 'libopus' | 'libvorbis' | 'flac' | 'pcm_s16le' | 'copy' | string;
/**
* Output format/container
*/
export type TOutputFormat = 'mp4' | 'webm' | 'mkv' | 'avi' | 'mov' | 'flv' | 'mp3' | 'wav' | 'ogg' | 'flac' | 'm4a' | 'gif' | string;
/**
* Preset for encoding speed/quality tradeoff
*/
export type TPreset = 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow';
/**
* Media information from ffprobe
*/
export interface IMediaInfo {
format: {
filename: string;
formatName: string;
formatLongName: string;
duration: number;
size: number;
bitrate: number;
};
streams: IStreamInfo[];
}
/**
* Stream information
*/
export interface IStreamInfo {
index: number;
codecName: string;
codecLongName: string;
codecType: 'video' | 'audio' | 'subtitle' | 'data';
width?: number;
height?: number;
frameRate?: number;
bitrate?: number;
sampleRate?: number;
channels?: number;
duration?: number;
}
/**
* Conversion options
*/
export interface IConversionOptions {
/** Output format/container */
format?: TOutputFormat;
/** Video codec */
videoCodec?: TVideoCodec;
/** Audio codec */
audioCodec?: TAudioCodec;
/** Video bitrate (e.g., '1M', '2000k') */
videoBitrate?: string;
/** Audio bitrate (e.g., '128k', '320k') */
audioBitrate?: string;
/** Output width (height auto-scaled if not specified) */
width?: number;
/** Output height (width auto-scaled if not specified) */
height?: number;
/** Frame rate */
fps?: number;
/** Audio sample rate in Hz */
sampleRate?: number;
/** Audio channels (1 for mono, 2 for stereo) */
audioChannels?: number;
/** Encoding preset (speed/quality tradeoff) */
preset?: TPreset;
/** Constant Rate Factor for quality (0-51, lower is better) */
crf?: number;
/** Start time in seconds or timecode string */
startTime?: number | string;
/** Duration in seconds or timecode string */
duration?: number | string;
/** Remove audio track */
noAudio?: boolean;
/** Remove video track */
noVideo?: boolean;
/** Additional ffmpeg arguments */
extraArgs?: string[];
/** Overwrite output file if exists */
overwrite?: boolean;
}
/**
* Progress information during conversion
*/
export interface IProgressInfo {
/** Current frame number */
frame: number;
/** Frames per second being processed */
fps: number;
/** Current quality metric */
q: number;
/** Output file size so far */
size: number;
/** Current time position in seconds */
time: number;
/** Current bitrate */
bitrate: string;
/** Processing speed (e.g., 1.5x realtime) */
speed: string;
/** Progress percentage (0-100) if duration known */
percent?: number;
}
/**
* Screenshot options
*/
export interface IScreenshotOptions {
/** Time position in seconds or timecode string */
time: number | string;
/** Output width */
width?: number;
/** Output height */
height?: number;
/** Output format (default: 'png') */
format?: 'png' | 'jpg' | 'webp';
/** Quality for jpg/webp (1-100) */
quality?: number;
}
/**
* Thumbnail generation options
*/
export interface IThumbnailOptions {
/** Number of thumbnails to generate */
count: number;
/** Output width */
width?: number;
/** Output height */
height?: number;
/** Output format */
format?: 'png' | 'jpg' | 'webp';
/** Filename pattern (use %d for number) */
filenamePattern?: string;
}

View File

@@ -1,2 +0,0 @@
export {};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2ludGVyZmFjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -1,9 +0,0 @@
import * as path from 'path';
import * as child_process from 'child_process';
export { path, child_process };
import * as smartfs from '@push.rocks/smartfs';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
export { smartfs, smartpath, smartpromise };
export declare const ffmpegBinaryPath: string;
export declare const ffprobeBinaryPath: string;

View File

@@ -1,15 +0,0 @@
// Node native modules
import * as path from 'path';
import * as child_process from 'child_process';
import { createRequire } from 'module';
export { path, child_process };
// @push.rocks scope
import * as smartfs from '@push.rocks/smartfs';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
export { smartfs, smartpath, smartpromise };
// ffmpeg static binaries - use createRequire for CommonJS compatibility
const require = createRequire(import.meta.url);
export const ffmpegBinaryPath = require('ffmpeg-static');
export const ffprobeBinaryPath = require('ffprobe-static').path;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsc0JBQXNCO0FBQ3RCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxhQUFhLE1BQU0sZUFBZSxDQUFDO0FBQy9DLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDdkMsT0FBTyxFQUFFLElBQUksRUFBRSxhQUFhLEVBQUUsQ0FBQztBQUUvQixvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLENBQUM7QUFFNUMsd0VBQXdFO0FBQ3hFLE1BQU0sT0FBTyxHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQy9DLE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFXLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztBQUNqRSxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBVyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxJQUFJLENBQUMifQ==

View File

@@ -1,6 +1,6 @@
{
"name": "@push.rocks/smartffmpeg",
"version": "1.1.0",
"version": "1.1.1",
"private": false,
"description": "A fast Node.js module for media file conversion using ffmpeg",
"main": "dist_ts/index.js",

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartffmpeg',
version: '1.1.0',
version: '1.1.1',
description: 'A fast Node.js module for media file conversion using ffmpeg'
}