diff --git a/.gitignore b/.gitignore index 8c9338d..2b1d3e1 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ sftp-config.json *.sublime-workspace .editorconfig .cache/ +package-lock.json \ No newline at end of file diff --git a/assets/src/blocks/rt-player/block.json b/assets/src/blocks/rt-player/block.json new file mode 100644 index 0000000..4d9882c --- /dev/null +++ b/assets/src/blocks/rt-player/block.json @@ -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" +} diff --git a/assets/src/blocks/rt-player/edit.js b/assets/src/blocks/rt-player/edit.js new file mode 100644 index 0000000..5c66c34 --- /dev/null +++ b/assets/src/blocks/rt-player/edit.js @@ -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 ( +
Video
+Thumbnail Image
+