mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 10:53: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 src="../packages/mp3-encoder/dist/bundles/mediabunny-mp3-encoder.js"></script>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
MediabunnyMp3Encoder.registerMp3Encoder();
|
//MediabunnyMp3Encoder.registerMp3Encoder();
|
||||||
|
|
||||||
const fileInput = document.createElement('input');
|
const fileInput = document.createElement('input');
|
||||||
fileInput.type = 'file';
|
fileInput.type = 'file';
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
chunked: true,
|
chunked: true,
|
||||||
chunkSize: 2**20
|
chunkSize: 2**20
|
||||||
});
|
});
|
||||||
const outputFormat = new Mediabunny.WebMOutputFormat({});
|
const outputFormat = new Mediabunny.Mp3OutputFormat({});
|
||||||
|
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.textContent = 'Cancel';
|
button.textContent = 'Cancel';
|
||||||
@@ -92,8 +92,8 @@
|
|||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
video: () => ({
|
video: () => ({
|
||||||
alpha: 'keep',
|
//alpha: 'keep',
|
||||||
width: 320,
|
//width: 320,
|
||||||
//discard: true,
|
//discard: true,
|
||||||
//discard: true,
|
//discard: true,
|
||||||
//crop: {
|
//crop: {
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
trim: {
|
trim: {
|
||||||
//start: 10,
|
start: 10,
|
||||||
//end: 20
|
//end: 20
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ It has the following features:
|
|||||||
- Video rotation
|
- Video rotation
|
||||||
- Video cropping
|
- Video cropping
|
||||||
- Video frame rate adjustment
|
- Video frame rate adjustment
|
||||||
|
- Video transparency removal/preservation
|
||||||
- Audio resampling
|
- Audio resampling
|
||||||
- Audio up/downmixing
|
- Audio up/downmixing
|
||||||
|
|
||||||
@@ -41,6 +42,14 @@ const output = new Output({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const conversion = await Conversion.init({ input, 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();
|
await conversion.execute();
|
||||||
|
|
||||||
// output.target.buffer contains the final file
|
// 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
|
- Copying media data whenever possible, otherwise transcoding it
|
||||||
- Dropping tracks that aren't supported in the output format
|
- 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
|
### 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.
|
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:
|
The following reasons exist:
|
||||||
|
|||||||
@@ -484,7 +484,14 @@ const output = new Output({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const conversion = await Conversion.init({ input, 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) => {
|
conversion.onProgress = (progress) => {
|
||||||
progress; // Number between 0 and 1, inclusive
|
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.
|
* If no function is set, the input's metadata tags will be copied to the output.
|
||||||
*/
|
*/
|
||||||
tags?: MetadataTags | ((inputTags: MetadataTags) => MaybePromise<MetadataTags>);
|
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 */
|
/** @internal */
|
||||||
_lastProgress = 0;
|
_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. */
|
/** The list of tracks that are included in the output file. */
|
||||||
readonly utilizedTracks: InputTrack[] = [];
|
readonly utilizedTracks: InputTrack[] = [];
|
||||||
/** The list of tracks from the input file that have been discarded, alongside the discard reason. */
|
/** 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') {
|
if (typeof options.tags === 'object') {
|
||||||
validateMetadataTags(options.tags);
|
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._options = options;
|
||||||
this.input = options.input;
|
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
|
// Now, let's deal with metadata tags
|
||||||
|
|
||||||
const inputTags = await this.input.getMetadataTags();
|
const inputTags = await this.input.getMetadataTags();
|
||||||
@@ -560,14 +569,105 @@ export class Conversion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.output.setMetadataTags(outputTags);
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isValid) {
|
||||||
|
warnElements.push('\n\n' + this._getInvalidityExplanation().join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnElements.length > 0) {
|
||||||
|
console.warn(...warnElements);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Executes the conversion process. Resolves once conversion is complete. */
|
/** @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() {
|
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) {
|
if (this._executed) {
|
||||||
throw new Error('Conversion cannot be executed twice.');
|
throw new Error('Conversion cannot be executed twice.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._executed = true;
|
this._executed = true;
|
||||||
|
|
||||||
if (this.onProgress) {
|
if (this.onProgress) {
|
||||||
|
|||||||
Reference in New Issue
Block a user