Skip to content

Commit 912d03a

Browse files
committed
Prepare video preview
1 parent 2d7da6e commit 912d03a

File tree

5 files changed

+219
-9
lines changed

5 files changed

+219
-9
lines changed

js/asset-edit.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

php/media/class-video.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ public function admin_enqueue_scripts() {
192192
$requiring_screens = array(
193193
'cloudinary_page_cloudinary_video_settings',
194194
'edit-tags',
195+
'toplevel_page_cloudinary',
195196
'term',
196197
);
197198

php/ui/component/class-asset-preview.php

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,17 @@ class Asset_Preview extends Asset {
3131
* @return array
3232
*/
3333
protected function preview( $struct ) {
34-
$image = filter_input( INPUT_GET, 'asset', FILTER_SANITIZE_NUMBER_INT );
35-
$dataset = $this->assets->get_asset( $image, 'dataset' );
34+
$attachment = filter_input( INPUT_GET, 'asset', FILTER_SANITIZE_NUMBER_INT );
35+
$dataset = $this->assets->get_asset( $attachment, 'dataset' );
3636

37+
// Check if the attachment is a video.
38+
$is_video = wp_attachment_is( 'video', $attachment );
39+
40+
if ( $is_video ) {
41+
$dataset['type'] = 'video';
42+
}
43+
44+
// Image preview structure.
3745
$struct['element'] = 'div';
3846
$struct['attributes']['id'] = 'cld-asset-edit';
3947
$struct['attributes']['data-item'] = $dataset;
@@ -51,12 +59,55 @@ public static function get_grid_options() {
5159
return array( 'north_west', 'north', 'north_east', 'west', 'center', 'east', 'south_west', 'south', 'south_east' );
5260
}
5361

62+
/**
63+
* Setup the JS data before rendering.
64+
*/
65+
protected function pre_render() {
66+
$attachment = filter_input( INPUT_GET, 'asset', FILTER_SANITIZE_NUMBER_INT );
67+
68+
// Check if the attachment is a video.
69+
if ( $attachment && wp_attachment_is( 'video', $attachment ) ) {
70+
// Get the actual video URL.
71+
$video_url = wp_get_attachment_url( $attachment );
72+
73+
// Setup video preview JavaScript.
74+
$url = CLOUDINARY_ENDPOINTS_PREVIEW_VIDEO;
75+
$preview_src = $url;
76+
$script_data = array(
77+
'url' => $url,
78+
'preview_url' => $preview_src,
79+
'file' => $video_url,
80+
'error' => esc_html__( 'Invalid transformations or error loading preview.', 'cloudinary' ),
81+
'valid_types' => \Cloudinary\Connect\Api::$transformation_index['video'],
82+
);
83+
wp_add_inline_script( 'cloudinary', 'var CLD_GLOBAL_TRANSFORMATIONS = CLD_GLOBAL_TRANSFORMATIONS ? CLD_GLOBAL_TRANSFORMATIONS : {};', 'before' );
84+
wp_add_inline_script( 'cloudinary', 'CLD_GLOBAL_TRANSFORMATIONS.video = ' . wp_json_encode( $script_data ), 'before' );
85+
86+
// Get the actual cloud name from the plugin configuration.
87+
$plugin = get_plugin_instance();
88+
$cloud_name = $plugin->get_component( 'connect' )->get_cloud_name();
89+
90+
$player = array();
91+
$player[] = 'var cld = cloudinary.Cloudinary.new({ cloud_name: \'' . esc_js( $cloud_name ) . '\', analytics: false });';
92+
wp_add_inline_script( 'cld-player', implode( $player ) );
93+
}
94+
95+
parent::pre_render();
96+
}
97+
5498
/**
5599
* Enqueue scripts this component may use.
56100
*/
57101
public function enqueue_scripts() {
58102
$plugin = get_plugin_instance();
59103
wp_enqueue_script( 'cloudinary-asset-edit', $plugin->dir_url . 'js/asset-edit.js', array(), $plugin->version, true );
60104
wp_enqueue_media();
105+
106+
// Check if the attachment is a video and enqueue video player assets.
107+
$attachment = filter_input( INPUT_GET, 'asset', FILTER_SANITIZE_NUMBER_INT );
108+
if ( $attachment && wp_attachment_is( 'video', $attachment ) ) {
109+
wp_enqueue_style( 'cld-player' );
110+
wp_enqueue_script( 'cld-player' );
111+
}
61112
}
62113
}

src/js/asset-edit.js

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { __ } from '@wordpress/i18n';
22
import AssetPreview from './components/asset-preview';
3+
import VideoAssetPreview from './components/video-asset-preview';
34
import AssetEditor from './components/asset-editor';
45

56
const AssetEdit = {
67
wrap: document.getElementById( 'cld-asset-edit' ),
8+
isVideo: false,
79
preview: null,
810
id: null,
911
editor: null,
@@ -60,7 +62,13 @@ const AssetEdit = {
6062
return;
6163
}
6264

63-
this.publicId = '/' + item.file.split('/').slice(-2).join('/');
65+
this.isVideo = item?.type === 'video';
66+
67+
if( this.isVideo ) {
68+
this.publicId = item?.data?.public_id;
69+
} else {
70+
this.publicId = '/' + item.file.split('/').slice(-2).join('/');
71+
}
6472

6573
// Set up centralized text overlay mapping as a property
6674
this.textOverlayMap = [
@@ -99,9 +107,16 @@ const AssetEdit = {
99107
this.initRemoveOverlayButtons();
100108
},
101109
initPreview() {
102-
this.preview = AssetPreview.init();
103-
this.wrap.appendChild( this.preview.createPreview( '100%', 'auto' ) );
104-
this.preview.setSrc( this.buildSrc(), true );
110+
if ( this.isVideo ) {
111+
this.preview = VideoAssetPreview.init();
112+
this.wrap.appendChild( this.preview.createPreview( 480, 360 ) );
113+
this.preview.setPublicId( this.publicId );
114+
this.preview.setSrc( this.buildSrc(), true );
115+
} else {
116+
this.preview = AssetPreview.init();
117+
this.wrap.appendChild( this.preview.createPreview( '100%', 'auto' ) );
118+
this.preview.setSrc( this.buildSrc(), true );
119+
}
105120

106121
this.transformationsInput.addEventListener( 'input', ( ev ) => {
107122
this.preview.setSrc( this.buildSrc() );
@@ -479,6 +494,9 @@ const AssetEdit = {
479494
buildSrc() {
480495
const imageOverlay = this.buildImageOverlay();
481496
const textOverlay = this.buildTextOverlay();
497+
const transformations = this.transformationsInput.value;
498+
499+
// For images, build the full URL
482500
const urlParts = [this.base];
483501
const htmlParts = [];
484502

@@ -492,23 +510,46 @@ const AssetEdit = {
492510
};
493511

494512
// Add transformations
495-
if (this.transformationsInput.value) {
496-
addPart(this.transformationsInput.value, 'string-preview-transformations', `.../${this.transformationsInput.value}`, false);
513+
if (transformations) {
514+
addPart(transformations, 'string-preview-transformations', `.../${transformations}`, false);
497515
} else {
498516
htmlParts.push(`<span class="string-preview-transformations string-preview-base">...</span>`);
499517
}
500518

501519
// Add overlays
502520
addPart(imageOverlay, 'string-preview-image-overlay');
503521
addPart(textOverlay, 'string-preview-text-overlay');
522+
504523
addPart(this.publicId, 'string-preview-public-id', this.publicId, false);
505524

506525
const previewUrl = urlParts.join('/').replace(/\/+/g, '/');
507526
this.assetPreviewTransformationString.innerHTML = htmlParts.join('');
508527
this.assetPreviewTransformationString.href = previewUrl;
509528

529+
// For videos, return only the transformation string (without base and publicId)
530+
if ( this.isVideo ) {
531+
return this.videoTransformations( transformations, imageOverlay, textOverlay );
532+
}
533+
510534
return previewUrl;
511535
},
536+
videoTransformations(transformations, imageOverlay, textOverlay) {
537+
const transformationParts = [];
538+
539+
if (transformations) {
540+
transformationParts.push(transformations);
541+
}
542+
543+
if (imageOverlay) {
544+
transformationParts.push(imageOverlay);
545+
}
546+
547+
if (textOverlay) {
548+
transformationParts.push(textOverlay);
549+
}
550+
551+
return transformationParts.join('/');
552+
},
512553
getOverlayData(map) {
513554
const overlay = {};
514555

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { __ } from '@wordpress/i18n';
2+
3+
const VideoAssetPreview = {
4+
preview: null,
5+
wrap: null,
6+
apply: null,
7+
url: null,
8+
publicId: null,
9+
player: null,
10+
defaultWidth: null,
11+
defaultHeight: null,
12+
maxSize: null,
13+
14+
init() {
15+
return this;
16+
},
17+
18+
createPreview( width = 427, height = 240 ) {
19+
this.maxSize = width > height ? width : height;
20+
this.defaultWidth = width;
21+
this.defaultHeight = height;
22+
this.wrap = document.createElement( 'div' );
23+
this.apply = document.createElement( 'button' );
24+
this.preview = document.createElement( 'video' );
25+
26+
this.apply.type = 'button';
27+
this.apply.classList.add( 'button-primary' );
28+
this.apply.innerText = __( 'Preview', 'cloudinary' );
29+
30+
this.preview.id = 'cld-asset-video-preview';
31+
this.preview.style.transition = 'opacity 1s';
32+
this.preview.style.opacity = 1;
33+
this.preview.style.maxWidth = '100%';
34+
this.preview.style.maxHeight = '100%';
35+
this.preview.controls = true;
36+
this.preview.setAttribute( 'width', width );
37+
this.preview.setAttribute( 'height', height );
38+
39+
this.wrap.style.minHeight = '200px';
40+
this.wrap.style.width = this.maxSize + 'px';
41+
this.wrap.style.position = 'relative';
42+
this.wrap.style.display = 'flex';
43+
this.wrap.style.alignItems = 'center';
44+
this.wrap.style.justifyContent = 'center';
45+
this.apply.style.position = 'absolute';
46+
this.apply.style.display = 'none';
47+
48+
this.wrap.appendChild( this.preview );
49+
this.wrap.appendChild( this.apply );
50+
51+
this.apply.addEventListener( 'click', () => {
52+
this.apply.style.display = 'none';
53+
this.preview.style.opacity = 0.6;
54+
this.updatePlayer( this.url );
55+
} );
56+
57+
return this.wrap;
58+
},
59+
60+
setPublicId( publicId ) {
61+
this.publicId = publicId;
62+
this.initPlayer();
63+
},
64+
65+
initPlayer() {
66+
if (
67+
typeof window.cloudinary === 'undefined' ||
68+
typeof window.cld === 'undefined'
69+
) {
70+
console.error( 'Cloudinary video player not loaded' );
71+
return;
72+
}
73+
74+
// Initialize player if not already initialized
75+
if ( ! this.player ) {
76+
this.player = window.cld.videoPlayer( this.preview.id, {
77+
fluid: true,
78+
controls: true,
79+
} );
80+
81+
// Set initial source immediately
82+
if ( this.publicId ) {
83+
this.player.source( this.publicId );
84+
}
85+
}
86+
},
87+
88+
setSrc( src, load = false ) {
89+
this.preview.style.opacity = 0.6;
90+
if ( load ) {
91+
this.apply.style.display = 'none';
92+
this.updatePlayer( src );
93+
} else {
94+
this.apply.style.display = 'block';
95+
this.url = src;
96+
}
97+
},
98+
99+
updatePlayer( src ) {
100+
if ( ! this.player ) {
101+
return;
102+
}
103+
104+
const sourceConfig = {
105+
publicId: this.publicId,
106+
};
107+
108+
if ( src && src.trim() !== '' ) {
109+
sourceConfig.transformation = { raw_transformation: src };
110+
}
111+
112+
this.player.source( sourceConfig );
113+
this.preview.style.opacity = 1;
114+
},
115+
};
116+
117+
export default VideoAssetPreview;

0 commit comments

Comments
 (0)