mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-03 05:43:50 +02:00
Add isValid field to Conversion if output track counts haven't been reached, improve error messages to be more clear (closes #148)
This commit is contained in:
+5
-5
@@ -4,7 +4,7 @@
|
||||
<script src="../packages/mp3-encoder/dist/bundles/mediabunny-mp3-encoder.js"></script>
|
||||
|
||||
<script type="module">
|
||||
MediabunnyMp3Encoder.registerMp3Encoder();
|
||||
//MediabunnyMp3Encoder.registerMp3Encoder();
|
||||
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
@@ -24,7 +24,7 @@
|
||||
chunked: true,
|
||||
chunkSize: 2**20
|
||||
});
|
||||
const outputFormat = new Mediabunny.WebMOutputFormat({});
|
||||
const outputFormat = new Mediabunny.Mp3OutputFormat({});
|
||||
|
||||
const button = document.createElement('button');
|
||||
button.textContent = 'Cancel';
|
||||
@@ -92,8 +92,8 @@
|
||||
},
|
||||
*/
|
||||
video: () => ({
|
||||
alpha: 'keep',
|
||||
width: 320,
|
||||
//alpha: 'keep',
|
||||
//width: 320,
|
||||
//discard: true,
|
||||
//discard: true,
|
||||
//crop: {
|
||||
@@ -146,7 +146,7 @@
|
||||
}
|
||||
},
|
||||
trim: {
|
||||
//start: 10,
|
||||
start: 10,
|
||||
//end: 20
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ It has the following features:
|
||||
- Video rotation
|
||||
- Video cropping
|
||||
- Video frame rate adjustment
|
||||
- Video transparency removal/preservation
|
||||
- Audio resampling
|
||||
- Audio up/downmixing
|
||||
|
||||
@@ -41,6 +42,14 @@ const output = new Output({
|
||||
});
|
||||
|
||||
const conversion = await Conversion.init({ input, output });
|
||||
if (!conversion.isValid) {
|
||||
// Conversion is invalid and cannot be executed without error.
|
||||
// This field gives reasons for why tracks were discarded:
|
||||
conversion.discardedTracks; // => DiscardedTrack[]
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await conversion.execute();
|
||||
|
||||
// output.target.buffer contains the final file
|
||||
@@ -57,7 +66,7 @@ Unconfigured, the conversion process handles all the details automatically, such
|
||||
- Copying media data whenever possible, otherwise transcoding it
|
||||
- Dropping tracks that aren't supported in the output format
|
||||
|
||||
You should consider inspecting the [discarded tracks](#discarded-tracks) before executing a `Conversion`.
|
||||
You should consider inspecting `isValid` and the [discarded tracks](#discarded-tracks) before executing a `Conversion`.
|
||||
|
||||
### Monitoring progress
|
||||
|
||||
@@ -353,6 +362,8 @@ type DiscardedTrack = {
|
||||
|
||||
Since you can inspect this list before executing a `Conversion`, this gives you the option to decide if you still want to move forward with the conversion process.
|
||||
|
||||
If `isValid` is `false`, then the discarded tracks caused the `Conversion` to become invalid. For example, this can happen when a format requires a specific codec but that codec cannot be encoded.
|
||||
|
||||
---
|
||||
|
||||
The following reasons exist:
|
||||
|
||||
@@ -484,7 +484,14 @@ const output = new Output({
|
||||
});
|
||||
|
||||
const conversion = await Conversion.init({ input, output });
|
||||
conversion.discardedTracks; // List of tracks that won't make it into the output
|
||||
if (!conversion.isValid) {
|
||||
// The conversion isn't possible and would error upon execution.
|
||||
// Check `discardedTracks` for the reasons.
|
||||
return;
|
||||
}
|
||||
|
||||
// List of tracks that won't make it into the output:
|
||||
conversion.discardedTracks;
|
||||
|
||||
conversion.onProgress = (progress) => {
|
||||
progress; // Number between 0 and 1, inclusive
|
||||
|
||||
+108
-8
@@ -96,6 +96,13 @@ export type ConversionOptions = {
|
||||
* If no function is set, the input's metadata tags will be copied to the output.
|
||||
*/
|
||||
tags?: MetadataTags | ((inputTags: MetadataTags) => MaybePromise<MetadataTags>);
|
||||
|
||||
/**
|
||||
* Whether to show potential console warnings about discarded tracks after calling `Conversion.init()`, defaults to
|
||||
* `true`. Set this to `false` if you're properly handling the `discardedTracks` and `isValid` fields already and
|
||||
* want to keep the console output clean.
|
||||
*/
|
||||
showWarnings?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -380,6 +387,11 @@ export class Conversion {
|
||||
/** @internal */
|
||||
_lastProgress = 0;
|
||||
|
||||
/**
|
||||
* Whether this conversion, as it has been configured, is valid and can be executed. If this field is `false`, check
|
||||
* the `discardedTracks` field for reasons.
|
||||
*/
|
||||
isValid = false;
|
||||
/** The list of tracks that are included in the output file. */
|
||||
readonly utilizedTracks: InputTrack[] = [];
|
||||
/** The list of tracks from the input file that have been discarded, alongside the discard reason. */
|
||||
@@ -449,6 +461,9 @@ export class Conversion {
|
||||
if (typeof options.tags === 'object') {
|
||||
validateMetadataTags(options.tags);
|
||||
}
|
||||
if (options.showWarnings !== undefined && typeof options.showWarnings !== 'boolean') {
|
||||
throw new TypeError('options.showWarnings, when provided, must be a boolean.');
|
||||
}
|
||||
|
||||
this._options = options;
|
||||
this.input = options.input;
|
||||
@@ -527,12 +542,6 @@ export class Conversion {
|
||||
}
|
||||
}
|
||||
|
||||
const unintentionallyDiscardedTracks = this.discardedTracks.filter(x => x.reason !== 'discarded_by_user');
|
||||
if (unintentionallyDiscardedTracks.length > 0) {
|
||||
// Let's give the user a notice/warning about discarded tracks so they aren't confused
|
||||
console.warn('Some tracks had to be discarded from the conversion:', unintentionallyDiscardedTracks);
|
||||
}
|
||||
|
||||
// Now, let's deal with metadata tags
|
||||
|
||||
const inputTags = await this.input.getMetadataTags();
|
||||
@@ -560,14 +569,105 @@ export class Conversion {
|
||||
}
|
||||
|
||||
this.output.setMetadataTags(outputTags);
|
||||
|
||||
// Let's check if the conversion can actually be executed
|
||||
this.isValid = this._totalTrackCount >= outputTrackCounts.total.min
|
||||
&& this._addedCounts.video >= outputTrackCounts.video.min
|
||||
&& this._addedCounts.audio >= outputTrackCounts.audio.min
|
||||
&& this._addedCounts.subtitle >= outputTrackCounts.subtitle.min;
|
||||
|
||||
if (this._options.showWarnings ?? true) {
|
||||
const warnElements: unknown[] = [];
|
||||
|
||||
const unintentionallyDiscardedTracks = this.discardedTracks.filter(x => x.reason !== 'discarded_by_user');
|
||||
if (unintentionallyDiscardedTracks.length > 0) {
|
||||
// Let's give the user a notice/warning about discarded tracks so they aren't confused
|
||||
warnElements.push(
|
||||
'Some tracks had to be discarded from the conversion:', unintentionallyDiscardedTracks,
|
||||
);
|
||||
}
|
||||
|
||||
/** Executes the conversion process. Resolves once conversion is complete. */
|
||||
if (!this.isValid) {
|
||||
warnElements.push('\n\n' + this._getInvalidityExplanation().join(''));
|
||||
}
|
||||
|
||||
if (warnElements.length > 0) {
|
||||
console.warn(...warnElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getInvalidityExplanation() {
|
||||
const elements: string[] = [];
|
||||
|
||||
if (this.discardedTracks.length === 0) {
|
||||
elements.push(
|
||||
'Due to missing tracks, this conversion cannot be executed.',
|
||||
);
|
||||
} else {
|
||||
const encodabilityIsTheProblem = this.discardedTracks.every(x =>
|
||||
x.reason === 'discarded_by_user' || x.reason === 'no_encodable_target_codec',
|
||||
);
|
||||
|
||||
elements.push(
|
||||
'Due to discarded tracks, this conversion cannot be executed.',
|
||||
);
|
||||
|
||||
if (encodabilityIsTheProblem) {
|
||||
const codecs = this.discardedTracks.flatMap((x) => {
|
||||
if (x.reason === 'discarded_by_user') return [];
|
||||
|
||||
if (x.track.type === 'video') {
|
||||
return this.output.format.getSupportedVideoCodecs();
|
||||
} else if (x.track.type === 'audio') {
|
||||
return this.output.format.getSupportedAudioCodecs();
|
||||
} else {
|
||||
return this.output.format.getSupportedSubtitleCodecs();
|
||||
}
|
||||
});
|
||||
|
||||
if (codecs.length === 1) {
|
||||
elements.push(
|
||||
`\nTracks were discarded because your environment is not able to encode '${codecs[0]}'.`,
|
||||
);
|
||||
} else {
|
||||
elements.push(
|
||||
'\nTracks were discarded because your environment is not able to encode any of the following'
|
||||
+ ` codecs: ${codecs.map(x => `'${x}'`).join(', ')}.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (codecs.includes('mp3')) {
|
||||
elements.push(
|
||||
`\nThe @mediabunny/mp3-encoder extension package provides support for encoding MP3.`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
elements.push('\nCheck the discardedTracks field for more info.');
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the conversion process. Resolves once conversion is complete.
|
||||
*
|
||||
* Will throw if `isValid` is `false`.
|
||||
*/
|
||||
async execute() {
|
||||
if (!this.isValid) {
|
||||
throw new Error(
|
||||
'Cannot execute this conversion because its output configuration is invalid. Make sure to always check'
|
||||
+ ' the isValid field before executing a conversion.\n'
|
||||
+ this._getInvalidityExplanation().join(''),
|
||||
);
|
||||
}
|
||||
|
||||
if (this._executed) {
|
||||
throw new Error('Conversion cannot be executed twice.');
|
||||
}
|
||||
|
||||
this._executed = true;
|
||||
|
||||
if (this.onProgress) {
|
||||
|
||||
Reference in New Issue
Block a user