How the Convert Project Defines File Formats and Their Capabilities
The convert project defines file formats and their capabilities using immutable FormatDefinition objects for static metadata and FileFormat instances for runtime conversion flags, enabling handlers to declaratively specify exactly what they can read and write.
The p2r3/convert repository implements a type-safe, extensible architecture for multimedia transformation. At its foundation, the system defines file formats and their capabilities through a strict separation between unchanging format properties and dynamic handler abilities. This design allows the conversion engine to build transformation graphs without hardcoding format logic into individual handlers.
Core Architecture: Definitions and Capabilities
The system splits format handling into two distinct layers located in src/FormatHandler.ts.
IFormatDefinition stores immutable metadata for a file type: its full name, short label, file extension, MIME type, and category. This data remains constant across all handlers.
FileFormat extends these properties with runtime capabilities. According to the source code in src/FormatHandler.ts lines 5-24, each FileFormat object tracks three critical boolean flags:
from– the handler can use this format as conversion inputto– the handler can produce this format as outputlossless– optional flag indicating whether the transformation preserves original quality
The FormatDefinition class implements IFormatDefinition and serves as a factory for creating FileFormat instances. It exposes two distinct APIs for binding capabilities: the concise supported() method and the flexible builder() pattern.
Pre-Defined Formats and Categories
Common file types are catalogued in src/CommonFormats.ts alongside a Category classification system. Categories group formats logically—such as Category.IMAGE, Category.AUDIO, or Category.VIDEO—enabling UI filtering and organizational features.
Each entry in CommonFormats is a FormatDefinition instance containing no capability data until bound to a handler. For example, the PNG definition in src/CommonFormats.ts lines 21-27 appears as:
export const PNG = new FormatDefinition(
"Portable Network Graphics",
"png",
"png",
"image/png",
Category.IMAGE
);
Declaring Conversion Capabilities
Handlers register their supported transformations using the supported() method defined in src/FormatHandler.ts lines 64-73. This method accepts a handler identifier, a from boolean, and a to boolean, returning a configured FileFormat instance.
// Handler that reads PNG but writes JPEG
supportedFormats = [
PNG.supported("png_reader", true, false), // from: true, to: false
JPEG.supported("jpeg_encoder", false, true) // from: false, to: true
];
An optional fourth parameter enables the lossless flag. The conversion engine—specifically src/TraversionGraph.ts—uses these from and to flags to construct a directed graph of possible transformations, automatically routing files through intermediate formats when direct conversion is unavailable.
Custom Formats with the Builder API
For specialized handlers requiring modified metadata—such as variant MIME types or custom quality profiles—the builder() API provides a fluent interface. Implemented in src/FormatHandler.ts lines 77-135, this pattern allows incremental construction of FileFormat objects.
const customWebP = CommonFormats.WEBP.builder("webp_encoder")
.allowFrom(false)
.allowTo(true)
.markLossless()
.override({ mime: "image/webp;type=optimized" })
.build();
Key builder methods include:
allowFrom(boolean)– sets input capabilityallowTo(boolean)– sets output capabilitymarkLossless()– enables the lossless quality flagoverride(partial)– tweaks immutable properties like MIME type or display namebuild()– finalizes and returns theFileFormatinstance
How Handlers Register Support
Every conversion handler implements the FormatHandler interface exported from src/FormatHandler.ts. This contract requires a supportedFormats array containing FileFormat objects that declare the handler's specific abilities.
export const ffmpegHandler: FormatHandler = {
name: "FFmpeg",
supportedFormats: [
CommonFormats.MP4.supported("ffmpeg_mp4", true, true),
CommonFormats.MP3.supported("ffmpeg_mp3", true, true)
],
// init, doConvert implementations...
};
Because FormatDefinition instances are pure data holders with no side effects, the system remains deterministic and unit-testable. The conversion engine aggregates supportedFormats arrays across all registered handlers to determine the complete topology of available transformations.
Summary
- Immutable metadata lives in
FormatDefinitionobjects (src/FormatHandler.ts), storing names, extensions, MIME types, and categories independently of handlers - Runtime capabilities are captured in
FileFormatinstances throughfrom,to, andlosslessboolean flags - Common formats are pre-defined in
src/CommonFormats.tswith standardized categories for logical grouping - Handlers declare support via the
supported()method for standard cases or thebuilder()API for customized variants - The conversion graph leverages these capability declarations to route files through valid transformation chains automatically
Frequently Asked Questions
What is the difference between FormatDefinition and FileFormat?
FormatDefinition stores static metadata about a file format—its name, extension, MIME type, and category—without any conversion logic. FileFormat extends this data with runtime capabilities specific to a handler, indicating whether that handler can read (from) or write (to) the format and whether the conversion is lossless.
How do I add support for a custom file format in convert?
Create a new FormatDefinition in src/CommonFormats.ts with the appropriate metadata and category, then register it in your handler using supported(handlerName, canRead, canWrite) or the builder() API if you need custom MIME types or quality flags. Finally, add the resulting FileFormat to your handler's supportedFormats array.
What does the lossless flag control in file format capabilities?
The lossless boolean flag, set via markLossless() in the builder or the fourth parameter of supported(), indicates whether conversions using this format preserve the exact source data without quality degradation. This metadata helps the conversion engine prioritize high-fidelity transformation paths when available.
Where are format categories defined and used?
Categories are defined as constants in src/CommonFormats.ts lines 3-13 and assigned to FormatDefinition instances during construction. They provide logical groupings—such as image, audio, video, or document—that the UI layer uses for filtering and organizing available conversion options.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →