Skip to content

Commit

Permalink
Ensure imported files are transcoded and compressed (#579)
Browse files Browse the repository at this point in the history
  • Loading branch information
swissspidy authored Jul 26, 2024
1 parent 88878df commit 95863ce
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 58 deletions.
19 changes: 18 additions & 1 deletion packages/editor/src/blockMediaPanel/importMedia.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
import { useDispatch, useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { isBlobURL } from '@wordpress/blob';
import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';

import type { Attachment } from '@mexp/media-utils';
import { store as uploadStore } from '@mexp/upload-media';
Expand All @@ -22,6 +23,7 @@ export function ImportMedia( { url, onChange }: ImportMediaProps ) {
const { baseControlProps, controlProps } = useBaseControlProps( {} );

const { addItemFromUrl } = useDispatch( uploadStore );
const { createErrorNotice } = useDispatch( noticesStore );
const isUploading = useIsUploadingByUrl( url );
const currentPostId = useSelect(
( select ) => select( editorStore ).getCurrentPostId(),
Expand All @@ -36,6 +38,21 @@ export function ImportMedia( { url, onChange }: ImportMediaProps ) {
void addItemFromUrl( {
url,
onChange: ( [ media ] ) => onChange( media ),
onError: ( err: Error ) => {
void createErrorNotice(
sprintf(
/* translators: %s: error message */
__(
'There was an error importing the file: %s',
'media-experiments'
),
err.message
),
{
type: 'snackbar',
}
);
},
additionalData: {
post: currentPostId,
mexp_media_source:
Expand Down
1 change: 1 addition & 0 deletions packages/ffmpeg/src/@types/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
declare global {
const FFMPEG_CDN_URL: string;
let SCRIPT_DEBUG: boolean;
}

export type {};
13 changes: 7 additions & 6 deletions packages/ffmpeg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ const FFMPEG_CONFIG = {

const ffmpegCoreUrl = FFMPEG_CDN_URL;

const isDevelopment =
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV !== 'production';
const isDevelopment = typeof SCRIPT_DEBUG !== 'undefined' && SCRIPT_DEBUG;

function readFile( file: File ): Promise< Uint8Array > {
const reader = new FileReader();
Expand Down Expand Up @@ -123,14 +120,18 @@ async function runFFmpegWithConfig(
// Delete file in MEMFS to free memory.
ffmpeg.FS( 'unlink', tempFileName );

// TODO: Consider using ffmpeg.setLogger() and look for messages such as "Decoder (codec av1) not found for input stream".
// Allows throwing with more detailed error message.
if ( ! data.buffer.byteLength ) {
throw new Error( `File ${ fileName } could not be processed` );
}

return new File(
[ new Blob( [ data.buffer ], { type: mimeType } ) ],
fileName,
{ type: mimeType }
);
} catch ( err ) {
// eslint-disable-next-line no-console -- We want to surface this error.
console.error( err );
throw err;
} finally {
try {
Expand Down
1 change: 1 addition & 0 deletions packages/upload-media/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ Optimizes/Compresses an existing video item.
_Parameters_

- _id_ `QueueItemId`: Item ID.
- _args_ `[OptimizeVideoItemArgs]`: Additional arguments for the operation.

#### pauseQueue

Expand Down
107 changes: 69 additions & 38 deletions packages/upload-media/src/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,17 @@ export function addItem( {

const itemId = uuidv4();

const blobUrl = createBlobURL( file );
dispatch< CacheBlobUrlAction >( {
type: Type.CacheBlobUrl,
id: itemId,
blobUrl,
} );
let blobUrl;

// StubFile could be coming from addItemFromUrl().
if ( ! ( file instanceof StubFile ) ) {
blobUrl = createBlobURL( file );
dispatch< CacheBlobUrlAction >( {
type: Type.CacheBlobUrl,
id: itemId,
blobUrl,
} );
}

dispatch< AddAction >( {
type: Type.Add,
Expand All @@ -260,11 +265,13 @@ export function addItem( {
blurHash,
dominantColor,
abortController: abortController || new AbortController(),
operations,
operations: Array.isArray( operations )
? operations
: [ OperationType.Prepare ],
},
} );

dispatch.prepareItem( itemId );
dispatch.processItem( itemId );
};
}

Expand Down Expand Up @@ -349,8 +356,8 @@ export function addItemFromUrl( {
sourceUrl: url,
operations: [
[ OperationType.FetchRemoteFile, { url, fileName } ],
OperationType.AddPoster,
OperationType.Upload,
// This will add the next steps, such as compression, poster generation, and upload.
OperationType.Prepare,
],
} );
};
Expand Down Expand Up @@ -402,12 +409,14 @@ export function addSideloadItem( {
...additionalData,
},
parentId,
operations,
operations: Array.isArray( operations )
? operations
: [ OperationType.Prepare ],
abortController: new AbortController(),
},
} );

dispatch.prepareItem( itemId );
dispatch.processItem( itemId );
};
}

Expand Down Expand Up @@ -505,7 +514,7 @@ export function muteExistingVideo( {
},
} );

dispatch.prepareItem( itemId );
dispatch.processItem( itemId );
};
}

Expand Down Expand Up @@ -576,7 +585,7 @@ export function addSubtitlesForExistingVideo( {
},
} );

dispatch.prepareItem( itemId );
dispatch.processItem( itemId );
};
}

Expand Down Expand Up @@ -640,7 +649,7 @@ export function addPosterForExistingVideo( {
},
} );

dispatch.prepareItem( itemId );
dispatch.processItem( itemId );
};
}

Expand Down Expand Up @@ -776,7 +785,7 @@ export function optimizeExistingItem( {
},
} );

dispatch.prepareItem( itemId );
dispatch.processItem( itemId );
};
}

Expand Down Expand Up @@ -903,6 +912,10 @@ export function processItem( id: QueueItemId ) {
} );

switch ( operation ) {
case OperationType.Prepare:
dispatch.prepareItem( item.id );
break;

case OperationType.ResizeCrop:
dispatch.resizeCropItem(
item.id,
Expand All @@ -923,7 +936,10 @@ export function processItem( id: QueueItemId ) {
break;

case OperationType.TranscodeVideo:
dispatch.optimizeVideoItem( item.id );
dispatch.optimizeVideoItem(
item.id,
operationArgs as OperationArgs[ OperationType.TranscodeVideo ]
);
break;

case OperationType.MuteVideo:
Expand Down Expand Up @@ -1104,13 +1120,19 @@ export function addPosterForItem( id: QueueItemId ) {
try {
switch ( mediaType ) {
case 'video':
const src = createBlobURL( item.file );
let src = isBlobURL( item.attachment?.url )
? item.attachment?.url
: undefined;

dispatch< CacheBlobUrlAction >( {
type: Type.CacheBlobUrl,
id,
blobUrl: src,
} );
if ( ! src ) {
src = createBlobURL( item.file );

dispatch< CacheBlobUrlAction >( {
type: Type.CacheBlobUrl,
id,
blobUrl: src,
} );
}

const poster = await getPosterFromVideo(
src,
Expand All @@ -1128,6 +1150,7 @@ export function addPosterForItem( id: QueueItemId ) {
dispatch.finishOperation( id, {
poster,
attachment: {
url: item.attachment?.url || src,
poster: posterUrl,
},
} );
Expand Down Expand Up @@ -1222,15 +1245,6 @@ export function prepareItem( id: QueueItemId ) {

const { file } = item;

// TODO: Check canTranscode either here, in muteExistingVideo, or in the UI.

// Transcoding type has already been set, e.g. via muteExistingVideo() or addSideloadItem().
// Also allow empty arrays, useful for example when sideloading original image.
if ( item.operations !== undefined ) {
dispatch.processItem( id );
return;
}

const mediaType = getMediaTypeFromMimeType( file.type );

const operations: Operation[] = [];
Expand Down Expand Up @@ -1320,7 +1334,11 @@ export function prepareItem( id: QueueItemId ) {
window.crossOriginIsolated &&
canProcessWithFFmpeg( file )
) {
operations.push( OperationType.TranscodeVideo );
operations.push( [
OperationType.TranscodeVideo,
// Don't make a fuzz if video cannot be transcoded.
{ continueOnError: true },
] );
}

operations.push(
Expand Down Expand Up @@ -1365,7 +1383,7 @@ export function prepareItem( id: QueueItemId ) {
operations,
} );

dispatch.processItem( id );
dispatch.finishOperation( id, {} );
};
}

Expand Down Expand Up @@ -1442,14 +1460,16 @@ export function uploadPoster( id: QueueItemId ) {
return;
}

// Video block expects such a structure for the poster.
// https://github.com/WordPress/gutenberg/blob/e0a413d213a2a829ece52c6728515b10b0154d8d/packages/block-library/src/video/edit.js#L154
// TODO: Pass poster ID as well so that the video block can update `featured_media` via the REST API.
const updatedAttachment = {
...attachment,
// Video block expects such a structure for the poster.
// https://github.com/WordPress/gutenberg/blob/e0a413d213a2a829ece52c6728515b10b0154d8d/packages/block-library/src/video/edit.js#L154
image: {
src: posterAttachment.url,
},
// Expected by ImportMedia / addItemFromUrl()
poster: posterAttachment.url,
};

// This might be confusing, but the idea is to update the original
Expand Down Expand Up @@ -1948,12 +1968,18 @@ export function optimizeImageItem(
};
}

type OptimizeVideoItemArgs = OperationArgs[ OperationType.TranscodeVideo ];

/**
* Optimizes/Compresses an existing video item.
*
* @param id Item ID.
* @param id Item ID.
* @param [args] Additional arguments for the operation.
*/
export function optimizeVideoItem( id: QueueItemId ) {
export function optimizeVideoItem(
id: QueueItemId,
args?: OptimizeVideoItemArgs
) {
return async ( { select, dispatch, registry }: ThunkArgs ) => {
const item = select.getItem( id ) as QueueItem;

Expand Down Expand Up @@ -1992,6 +2018,11 @@ export function optimizeVideoItem( id: QueueItemId ) {
},
} );
} catch ( error ) {
if ( args?.continueOnError ) {
dispatch.finishOperation( id, {} );
return;
}

dispatch.cancelItem(
id,
error instanceof Error
Expand Down
5 changes: 1 addition & 4 deletions packages/upload-media/src/store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ function reducer(
return {
...item,
operations: [
...action.operations,
...( item.operations || [] ),
...action.operations,
],
};
} ),
Expand All @@ -176,9 +176,6 @@ function reducer(
? {
...item.attachment,
...action.item.attachment,
// TODO: Update to pass this correctly.
// url: action.item?.url,
// mimeType: action.item?.file?.type,
}
: undefined;

Expand Down
5 changes: 2 additions & 3 deletions packages/upload-media/src/store/test/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ describe( 'actions', () => {
sourceFile: expect.any( File ),
status: ItemStatus.Processing,
attachment: {
url: expect.stringMatching( /^blob:/ ),
url: undefined,
},
operations: [
[
Expand All @@ -148,8 +148,7 @@ describe( 'actions', () => {
fileName: 'example.jpg',
},
],
OperationType.AddPoster,
OperationType.Upload,
OperationType.Prepare,
],
} )
);
Expand Down
4 changes: 2 additions & 2 deletions packages/upload-media/src/store/test/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ describe( 'reducer', () => {
} );

describe( `${ Type.AddOperations }`, () => {
it( 'prepends operations to the list', () => {
it( 'appends operations to the list', () => {
const initialState: State = {
imageSizes: {},
queueStatus: 'active',
Expand Down Expand Up @@ -282,9 +282,9 @@ describe( 'reducer', () => {
id: '1',
status: ItemStatus.Processing,
operations: [
OperationType.Upload,
OperationType.Compress,
OperationType.AddPoster,
OperationType.Upload,
],
},
],
Expand Down
Loading

0 comments on commit 95863ce

Please sign in to comment.