Skip to content

Commit

Permalink
feature: add rt-player block
Browse files Browse the repository at this point in the history
  • Loading branch information
pranvinit committed Nov 8, 2024
1 parent 970ef51 commit 3c83e8a
Show file tree
Hide file tree
Showing 19 changed files with 867 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ sftp-config.json
*.sublime-workspace
.editorconfig
.cache/
package-lock.json
42 changes: 42 additions & 0 deletions assets/src/blocks/rt-player/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "transcoder/rt-player",
"version": "1.0.0",
"title": "RT Player",
"category": "media",
"icon": "video-alt3",
"description": "A block for embedding videos with a custom player.",
"supports": {
"html": false
},
"attributes": {
"videoId": {
"type": "string"
},
"videoUrl": {
"type": "string"
},
"videoAlt": {
"type": "string"
},
"useCustomSize": {
"type": "boolean",
"default": false
},
"videoSize": {
"type": "object",
"default": {}
},
"videoType": {
"type": "string"
},
"videoPosterUrl": {
"type": "string"
}
},
"textdomain": "transcoder",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"viewScript": "file:./frontend.js"
}
272 changes: 272 additions & 0 deletions assets/src/blocks/rt-player/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
/**
* React hook that is used to mark the block wrapper element.
* It provides all the necessary props like the class name.
*
* @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps
*/

/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* WordPress dependencies
*/
import { useBlockProps, InspectorControls, MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button, PanelBody, PanelRow, TextControl, ToggleControl, Placeholder } from '@wordpress/components';
import { useEffect, useRef } from '@wordpress/element';

/**
* External dependencies
*/
import videojs from 'video.js';
import 'videojs-contrib-quality-levels';
import 'videojs-contrib-quality-menu';
import 'video.js/dist/video-js.css';
import 'videojs-contrib-quality-menu/dist/videojs-contrib-quality-menu.css';

/**
* Internal dependencies
*/
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* Those files can contain any CSS code that gets applied to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './editor.scss';

/**
* External dependencies
*/

/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @param {Object} props Block props.
* @param {Object} props.attributes Block's attributes.
* @param {Object} props.setAttributes Function to set block's attributes.
*
* @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#edit
*
* @return {HTMLElement} Element to render.
*/
export default function Edit( { attributes, setAttributes } ) {
const { videoId, videoUrl, videoAlt, useCustomSize, videoSize, videoType, videoPosterUrl } = attributes;
const videoRef = useRef( null );
const playerInstance = useRef( null );

useEffect( () => {
if ( ! videoUrl || ! videoRef.current || playerInstance.current ) {
return;
}

// Create a new video element.
const videoElement = document.createElement( 'video-js' );
videoElement.classList.add( 'vjs-big-play-centered' );
videoRef.current.appendChild( videoElement );

// Initialize the player.
const player = playerInstance.current = videojs( videoElement, {
controls: true,
responsive: ! useCustomSize,
fluid: ! useCustomSize,
poster: videoPosterUrl,
sources: [ {
src: videoUrl,
type: videoType,
} ],
preload: 'auto',
} );

// Initialize the Quality Menu plugin after the player is ready.
player.ready( () => {
if ( typeof player.qualityMenu === 'function' ) {
player.qualityMenu();
} else {
console.error( 'Quality Menu plugin is not available.' );
}
} );

const isCustomSizeDefined = videoSize.width && videoSize.height;

// Set initial dimensions if defined.
if ( useCustomSize && isCustomSizeDefined ) {
player.width( videoSize.width );
player.height( videoSize.height );
}

return () => {
if ( player && ! player.isDisposed() ) {
player.dispose();
playerInstance.current = null;
}
};
}, [ videoUrl, videoPosterUrl ] );

useEffect( () => {
if ( ! playerInstance.current ) {
return;
}

const isCustomSizeDefined = videoSize.width && videoSize.height;

if ( useCustomSize && isCustomSizeDefined ) {
// Disable responsive and fluid mode.
playerInstance.current.fluid( false );
playerInstance.current.responsive( false );

// Set custom size.
playerInstance.current.width( videoSize.width );
playerInstance.current.height( videoSize.height );
} else {
// Enable responsive and fluid mode.
playerInstance.current.fluid( true );
playerInstance.current.responsive( true );
}
}, [ useCustomSize, videoSize ] );

const onSelectVideo = ( media ) => {
setAttributes( {
mediaId: media.id,
videoUrl: media.url,
videoAlt: media.alt || media.title,
videoSize: {
width: media.width,
height: media.height,
},
videoType: media.mime,
videoPosterUrl: media.thumb?.src || media.image?.src,
} );
};

const onSelectPoster = ( media ) => {
setAttributes( {
videoPosterUrl: media.url,
} );
};

const onSizeChange = ( value, key ) => {
if ( isNaN( parseInt( value ) ) ) {
return;
}

setAttributes( {
videoSize: {
...videoSize,
[ key ]: parseInt( value ),
},
} );
};

return (
<div { ...useBlockProps() }>
<InspectorControls>
<PanelBody title="Video Settings">
<p><strong>Video</strong></p>
<PanelRow>
<MediaUploadCheck>
<MediaUpload
title="Select Video"
onSelect={ onSelectVideo }
allowedTypes={ [ 'video' ] }
accept="video/*"
value={ videoId }
render={ ( { open } ) => (
<div className="upload-controls">
<Button onClick={ open } variant="primary">
{ ! videoUrl ? __( 'Select Video' ) : __( 'Replace Video' ) }
</Button>
{ videoUrl && (
<Button onClick={ () => setAttributes( { videoUrl: null, videoPosterUrl: null } ) } variant="link" isDestructive>
{ __( 'Remove Video' ) }
</Button>
) }
</div>
) }
/>
</MediaUploadCheck>
</PanelRow>
<p><strong>Thumbnail Image</strong></p>
<PanelRow>
<MediaUploadCheck>
<MediaUpload
title="Select Thumbnail Image"
onSelect={ onSelectPoster }
allowedTypes={ [ 'image' ] }
accept="image/*"
render={ ( { open } ) => (
<div className="upload-controls">
<Button onClick={ open } variant="primary">
{ ! videoPosterUrl ? __( 'Select Thumbnail Image' ) : __( 'Replace Thumbnail Image' ) }
</Button>
{ videoPosterUrl && (
<Button onClick={ () => setAttributes( { videoPosterUrl: null } ) } variant="link" isDestructive>
{ __( 'Remove Thumbnail Image' ) }
</Button>
) }
</div>
) }
/>
</MediaUploadCheck>
</PanelRow>
<TextControl
label="Alt Text"
value={ videoAlt }
onChange={ ( value ) => setAttributes( { videoAlt: value } ) }
help="Descriptive text for screen readers and SEO."
/>
<ToggleControl
label="Use Custom Size"
checked={ useCustomSize }
onChange={ ( value ) => setAttributes( { useCustomSize: value } ) }
/>
{ useCustomSize && (
<>
<TextControl
label="Width"
value={ videoSize.width }
onChange={ ( value ) => onSizeChange( value, 'width' ) }
/>
<TextControl
label="Height"
value={ videoSize.height }
onChange={ ( value ) => onSizeChange( value, 'height' ) }
/>
</>
) }
</PanelBody>
</InspectorControls>
{ videoUrl ? (
<div data-vjs-player>
<div ref={ videoRef } />
</div>
) : (
<Placeholder
icon="format-video"
label="RT Player Block"
instructions="Select a video from your media library or upload a new one."
>
<MediaUploadCheck>
<MediaUpload
onSelect={ onSelectVideo }
allowedTypes={ [ 'video' ] }
render={ ( { open } ) => (
<Button onClick={ open } variant="primary">
Select Video
</Button>
) }
/>
</MediaUploadCheck>
</Placeholder>
) }
</div>
);
}
22 changes: 22 additions & 0 deletions assets/src/blocks/rt-player/editor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* The following styles get applied inside the editor only.
*
* Replace them with your own styles or remove the file completely.
*/

.block-editor-block-inspector {

.components-panel__row {
margin-bottom: 1em;

.upload-controls {
display: flex;
flex-direction: column;
gap: 1em;
}
}
}

.wp-block-trascoder-rt-player {
// Add editor styles here.
}
15 changes: 15 additions & 0 deletions assets/src/blocks/rt-player/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import metadata from './block.json';

export default {
name: metadata.name,
viewportWidth: 1200,
attributes: {},
};
24 changes: 24 additions & 0 deletions assets/src/blocks/rt-player/frontend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* global videojs */

// Adding an event listener for the 'DOMContentLoaded' event to ensure the script runs after the complete page is loaded.
document.addEventListener( 'DOMContentLoaded', () => rtPlayerFn() );

/**
* RT Player
*
*/
function rtPlayerFn() {
const players = document.querySelectorAll( '.video-js' );
players.forEach( ( playerEl ) => {
const player = videojs( playerEl );

// Initialize the quality menu.
player.ready( () => {
if ( typeof player.qualityMenu === 'function' ) {
player.qualityMenu();
} else {
console.error( 'Quality Menu plugin is not available.' );
}
} );
} );
}
Loading

0 comments on commit 3c83e8a

Please sign in to comment.