From c07ecccc8fe750221c23d22df374734c5fba3f83 Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Fri, 23 Sep 2022 08:30:33 +0100 Subject: [PATCH] Video Cropping: Add post meta for original (#12349) Co-authored-by: Pascal Birchler --- .phpstorm.meta.php | 1 + includes/Media/Cropping.php | 83 +++++++++++++++++++ includes/Plugin.php | 1 + includes/uninstall.php | 2 + .../media/utils/useMediaUploadQueue/index.js | 5 -- .../utils/useMediaUploadQueue/reducer.js | 21 ++--- .../utils/useMediaUploadQueue/test/reducer.js | 13 +-- .../src/app/media/utils/useProcessMedia.js | 2 + packages/wp-story-editor/src/api/media.js | 29 +++++-- .../integration/tests/Media/Cropping.php | 76 +++++++++++++++++ tests/phpunit/integration/tests/Uninstall.php | 10 ++- 11 files changed, 200 insertions(+), 43 deletions(-) create mode 100644 includes/Media/Cropping.php create mode 100644 tests/phpunit/integration/tests/Media/Cropping.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 507652a29420..0f1a9983bb5b 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -39,6 +39,7 @@ 'media.image_sizes' => \Google\Web_Stories\Media\Image_Sizes::class, 'media.media_source' => \Google\Web_Stories\Media\Media_Source_Taxonomy::class, 'media.video.captions' => \Google\Web_Stories\Media\Video\Captions::class, + 'media.cropping' => \Google\Web_Stories\Media\Cropping::class, 'media.video.muting' => \Google\Web_Stories\Media\Video\Muting::class, 'media.video.optimization' => \Google\Web_Stories\Media\Video\Optimization::class, 'media.video.poster' => \Google\Web_Stories\Media\Video\Poster::class, diff --git a/includes/Media/Cropping.php b/includes/Media/Cropping.php new file mode 100644 index 000000000000..9d1bea35f0ff --- /dev/null +++ b/includes/Media/Cropping.php @@ -0,0 +1,83 @@ +register_meta(); + add_action( 'delete_attachment', [ $this, 'delete_video' ] ); + } + + /** + * Register meta + * + * @since 1.26.0 + */ + public function register_meta(): void { + register_meta( + 'post', + self::CROPPED_ID_POST_META_KEY, + [ + 'sanitize_callback' => 'absint', + 'type' => 'integer', + 'description' => __( 'Parent ID if this is a cropped attachment', 'web-stories' ), + 'show_in_rest' => true, + 'default' => 0, + 'single' => true, + 'object_subtype' => 'attachment', + ] + ); + } + + /** + * Deletes associated meta data when a video is deleted. + * + * @since 1.26.0 + * + * @param int $attachment_id ID of the attachment to be deleted. + */ + public function delete_video( int $attachment_id ): void { + delete_metadata( 'post', 0, self::CROPPED_ID_POST_META_KEY, $attachment_id, true ); + } +} diff --git a/includes/Plugin.php b/includes/Plugin.php index ba3d553ad8cd..bf135dd9bbd2 100644 --- a/includes/Plugin.php +++ b/includes/Plugin.php @@ -98,6 +98,7 @@ class Plugin extends ServiceBasedPlugin { 'media.image_sizes' => Media\Image_Sizes::class, 'media.media_source' => Media\Media_Source_Taxonomy::class, 'media.video.captions' => Media\Video\Captions::class, + 'media.cropping' => Media\Cropping::class, 'media.video.muting' => Media\Video\Muting::class, 'media.video.optimization' => Media\Video\Optimization::class, 'media.video.poster' => Media\Video\Poster::class, diff --git a/includes/uninstall.php b/includes/uninstall.php index 902c75fb3134..160fe6d6fa35 100644 --- a/includes/uninstall.php +++ b/includes/uninstall.php @@ -28,6 +28,7 @@ use Google\Web_Stories\Media\Base_Color; use Google\Web_Stories\Media\Blurhash; +use Google\Web_Stories\Media\Cropping; use Google\Web_Stories\Media\Media_Source_Taxonomy; use Google\Web_Stories\Media\Video\Is_Gif; use Google\Web_Stories\Media\Video\Muting; @@ -127,6 +128,7 @@ function delete_stories_post_meta(): void { delete_post_meta_by_key( Optimization::OPTIMIZED_ID_POST_META_KEY ); delete_post_meta_by_key( Muting::MUTED_ID_POST_META_KEY ); delete_post_meta_by_key( Muting::IS_MUTED_POST_META_KEY ); + delete_post_meta_by_key( Cropping::CROPPED_ID_POST_META_KEY ); delete_post_meta_by_key( Trimming::TRIM_POST_META_KEY ); delete_post_meta_by_key( Blurhash::BLURHASH_POST_META_KEY ); delete_post_meta_by_key( Is_Gif::IS_GIF_POST_META_KEY ); diff --git a/packages/story-editor/src/app/media/utils/useMediaUploadQueue/index.js b/packages/story-editor/src/app/media/utils/useMediaUploadQueue/index.js index e82afb3f1a8e..f4997d8d8264 100644 --- a/packages/story-editor/src/app/media/utils/useMediaUploadQueue/index.js +++ b/packages/story-editor/src/app/media/utils/useMediaUploadQueue/index.js @@ -256,10 +256,6 @@ function useMediaUploadQueue() { return; } - additionalData.meta = { - ...additionalData.meta, - trimData, - }; finishTrimming({ id, file: newFile, additionalData }); } catch (error) { // Cancel uploading if there were any errors. @@ -318,7 +314,6 @@ function useMediaUploadQueue() { return; } - additionalData.isCropped = true; finishCropping({ id, file: newFile, posterFile, additionalData }); } catch (error) { // Cancel uploading if there were any errors. diff --git a/packages/story-editor/src/app/media/utils/useMediaUploadQueue/reducer.js b/packages/story-editor/src/app/media/utils/useMediaUploadQueue/reducer.js index 6004482779b3..1d73ff863a24 100644 --- a/packages/story-editor/src/app/media/utils/useMediaUploadQueue/reducer.js +++ b/packages/story-editor/src/app/media/utils/useMediaUploadQueue/reducer.js @@ -57,7 +57,7 @@ export function addItem( onUploadProgress, onUploadError, onUploadSuccess, - additionalData: _additionalData = {}, + additionalData = {}, posterFile, muteVideo, cropVideo, @@ -74,13 +74,6 @@ export function addItem( resource.id = uuidv4(); } - const additionalData = { - ..._additionalData, - meta: { - ...(_additionalData.meta || {}), - }, - }; - if ( resource.type === 'video' && resource.isMuted !== null && @@ -90,13 +83,13 @@ export function addItem( } if (resource?.baseColor) { - additionalData.meta.baseColor = resource.baseColor; + additionalData.baseColor = resource.baseColor; } - // Do not copy over BlurHash for new trimmed videos + // Do not copy over BlurHash for new trimmed and cropped videos // since the poster (and thus the BlurHash) might be different. - if (resource?.blurHash && !resource?.trimData) { - additionalData.meta.blurHash = resource.blurHash; + if (resource?.blurHash && !trimData && !cropVideo) { + additionalData.blurHash = resource.blurHash; } const newItem = { @@ -433,10 +426,6 @@ export function finishCropping( file, posterFile, state: ITEM_STATUS.CROPPED, - resource: { - ...item.resource, - isCropped: true, - }, additionalData: { ...item.additionalData, ...additionalData, diff --git a/packages/story-editor/src/app/media/utils/useMediaUploadQueue/test/reducer.js b/packages/story-editor/src/app/media/utils/useMediaUploadQueue/test/reducer.js index 2a6f8e4c5bd6..76c67ab787a3 100644 --- a/packages/story-editor/src/app/media/utils/useMediaUploadQueue/test/reducer.js +++ b/packages/story-editor/src/app/media/utils/useMediaUploadQueue/test/reducer.js @@ -75,9 +75,7 @@ describe('useMediaUploadQueue', () => { id: 456, foo: 'bar', }), - additionalData: { - meta: {}, - }, + additionalData: {}, }), ], }); @@ -113,7 +111,6 @@ describe('useMediaUploadQueue', () => { isMuted: false, }), additionalData: { - meta: {}, isMuted: false, }, }), @@ -151,9 +148,7 @@ describe('useMediaUploadQueue', () => { baseColor: 'barbaz', }), additionalData: { - meta: { - baseColor: 'barbaz', - }, + baseColor: 'barbaz', }, }), ], @@ -190,9 +185,7 @@ describe('useMediaUploadQueue', () => { blurHash: 'barbaz', }), additionalData: { - meta: { - blurHash: 'barbaz', - }, + blurHash: 'barbaz', }, }), ], diff --git a/packages/story-editor/src/app/media/utils/useProcessMedia.js b/packages/story-editor/src/app/media/utils/useProcessMedia.js index 549ffa530436..9870a2520be6 100644 --- a/packages/story-editor/src/app/media/utils/useProcessMedia.js +++ b/packages/story-editor/src/app/media/utils/useProcessMedia.js @@ -306,6 +306,7 @@ function useProcessMedia({ additionalData: { originalId: resourceId, isMuted, + trimData, mediaSource: isOptimized ? 'video-optimization' : 'editor', }, elementId, @@ -541,6 +542,7 @@ function useProcessMedia({ cropVideo: true, additionalData: { original_id: resourceId, + cropOriginId: resourceId, cropParams, mediaSource: isOptimized ? 'video-optimization' : 'editor', }, diff --git a/packages/wp-story-editor/src/api/media.js b/packages/wp-story-editor/src/api/media.js index 93af73dc2153..cd543c792d45 100644 --- a/packages/wp-story-editor/src/api/media.js +++ b/packages/wp-story-editor/src/api/media.js @@ -166,6 +166,10 @@ export function uploadMedia(config, file, additionalData) { mediaId, storyId, templateId, + optimizedId, + cropOriginId, + mutedId, + posterId, isMuted, mediaSource, trimData, @@ -180,17 +184,24 @@ export function uploadMedia(config, file, additionalData) { web_stories_is_muted: isMuted, post: templateId || storyId || mediaId, original_id: originalId, - web_stories_trim_data: trimData, - web_stories_base_color: baseColor, - web_stories_blurhash: blurHash, + meta: { + web_stories_base_color: baseColor, + web_stories_blurhash: blurHash, + web_stories_cropped_origin_id: cropOriginId, + web_stories_optimized_id: optimizedId, + web_stories_muted_id: mutedId, + web_stories_poster_id: posterId, + web_stories_trim_data: trimData, + web_stories_is_gif: isGif, + }, alt_text: altText, }; - if (isGif !== undefined) { - wpKeysMapping.meta = { - web_stories_is_gif: isGif, - }; - } + Object.entries(wpKeysMapping.meta).forEach(([key, value]) => { + if (value === undefined) { + delete wpKeysMapping.meta[key]; + } + }); Object.entries(wpKeysMapping).forEach(([key, value]) => { if (value === undefined) { @@ -228,6 +239,7 @@ export function updateMedia(config, mediaId, data) { isMuted, mediaSource, optimizedId, + cropOriginId, mutedId, posterId, storyId, @@ -240,6 +252,7 @@ export function updateMedia(config, mediaId, data) { web_stories_blurhash: blurHash, web_stories_optimized_id: optimizedId, web_stories_muted_id: mutedId, + web_stories_cropped_origin_id: cropOriginId, web_stories_poster_id: posterId, }, web_stories_is_muted: isMuted, diff --git a/tests/phpunit/integration/tests/Media/Cropping.php b/tests/phpunit/integration/tests/Media/Cropping.php new file mode 100644 index 000000000000..502fde405823 --- /dev/null +++ b/tests/phpunit/integration/tests/Media/Cropping.php @@ -0,0 +1,76 @@ +instance = new \Google\Web_Stories\Media\Cropping(); + } + + /** + * @covers ::register + */ + public function test_register(): void { + $this->instance->register(); + + $this->assertTrue( registered_meta_key_exists( 'post', $this->instance::CROPPED_ID_POST_META_KEY, 'attachment' ) ); + $this->assertSame( 10, has_action( 'delete_attachment', [ $this->instance, 'delete_video' ] ) ); + } + + /** + * @covers ::delete_video + */ + public function test_delete_video_meta_attachment_is_deleted(): void { + $video_attachment_id = self::factory()->attachment->create_object( + [ + 'file' => DIR_TESTDATA . '/uploads/test-video.mp4', + 'post_parent' => 0, + 'post_mime_type' => 'video/mp4', + 'post_title' => 'Test Video', + ] + ); + + $cropped_attachment_id = self::factory()->attachment->create_object( + [ + 'file' => DIR_TESTDATA . '/uploads/test-video.mp4', + 'post_parent' => 0, + 'post_mime_type' => 'image/jpeg', + 'post_title' => 'Test Image', + ] + ); + + add_post_meta( $video_attachment_id, $this->instance::CROPPED_ID_POST_META_KEY, $cropped_attachment_id ); + $this->assertSame( $cropped_attachment_id, (int) get_post_meta( $video_attachment_id, $this->instance::CROPPED_ID_POST_META_KEY, true ) ); + wp_delete_attachment( $cropped_attachment_id ); + $this->assertEmpty( get_post_meta( $video_attachment_id, $this->instance::CROPPED_ID_POST_META_KEY, true ) ); + } +} diff --git a/tests/phpunit/integration/tests/Uninstall.php b/tests/phpunit/integration/tests/Uninstall.php index 801aa358a89e..32be36ae1763 100644 --- a/tests/phpunit/integration/tests/Uninstall.php +++ b/tests/phpunit/integration/tests/Uninstall.php @@ -40,8 +40,9 @@ public function set_up(): void { $terms_ids = self::factory()->term->create_many( 5, [ 'taxonomy' => $source_taxonomy->get_taxonomy_slug() ] ); foreach ( self::$attachment_ids as $attachment_id ) { - add_post_meta( $attachment_id, 'web_stories_is_poster', '1' ); - add_post_meta( $attachment_id, 'web_stories_poster_id', '999' ); + add_post_meta( $attachment_id, \Google\Web_Stories\Media\Video\Poster::POSTER_POST_META_KEY, '1' ); + add_post_meta( $attachment_id, \Google\Web_Stories\Media\Video\Poster::POSTER_ID_POST_META_KEY, '999' ); + add_post_meta( $attachment_id, \Google\Web_Stories\Media\Cropping::CROPPED_ID_POST_META_KEY, '999' ); wp_set_object_terms( $attachment_id, $terms_ids, $source_taxonomy->get_taxonomy_slug() ); } @@ -115,8 +116,9 @@ public function test_delete_stories_post_meta(): void { \Google\Web_Stories\delete_stories_post_meta(); foreach ( self::$attachment_ids as $attachment_id ) { - $this->assertSame( '', get_post_meta( $attachment_id, 'web_stories_is_poster', true ) ); - $this->assertSame( 0, get_post_meta( $attachment_id, 'web_stories_poster_id', true ) ); + $this->assertSame( '', get_post_meta( $attachment_id, \Google\Web_Stories\Media\Video\Poster::POSTER_POST_META_KEY, true ) ); + $this->assertSame( 0, get_post_meta( $attachment_id, \Google\Web_Stories\Media\Video\Poster::POSTER_ID_POST_META_KEY, true ) ); + $this->assertSame( 0, get_post_meta( $attachment_id, \Google\Web_Stories\Media\Cropping::CROPPED_ID_POST_META_KEY, true ) ); } }