diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5957360f..eb5e6f163 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog #
+## v1.3.4 (August 11, 2025) ##
+
+- Fix: Video Editor UI issues
+
+## v1.3.3 (August 11, 2025) ##
+
+- Tweak: Improved video player controls and HLS URL handling
+- Tweak: Added Video SEO support and duplicate prevention for GoDAM Tab videos
+- Tweak: Player-specific video speed and quality settings
+- Fix: Resolved share button and JavaScript registration issues
+
## v1.3.2 (August 6, 2025) ##
- Fix: Resolved PHP Warning on admin pages
diff --git a/README.md b/README.md
index 384fe503c..26a6084ef 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@

# GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more
-Contributors: [rtcamp](https://profiles.wordpress.org/rtcamp), [elifvish](https://profiles.wordpress.org/elifvish), [subodhrajpopat](https://profiles.wordpress.org/subodhrajpopat), [kuldipchaudhary](https://profiles.wordpress.org/kuldipchaudhary), [prachigarg19](https://profiles.wordpress.org/prachigarg19), [juzar](https://profiles.wordpress.org/juzar), [geekofshire](https://profiles.wordpress.org/geekofshire), [nazmulhassann20](https://profiles.wordpress.org/nazmulhassann20), [abhinavbelhekar03](https://profiles.wordpress.org/abhinavbelhekar03), [gautam23](https://profiles.wordpress.org/gautam23)
+Contributors: [rtcamp](https://profiles.wordpress.org/rtcamp), [elifvish](https://profiles.wordpress.org/elifvish), [subodhrajpopat](https://profiles.wordpress.org/subodhrajpopat), [kuldipchaudhary](https://profiles.wordpress.org/kuldipchaudhary), [prachigarg19](https://profiles.wordpress.org/prachigarg19), [juzar](https://profiles.wordpress.org/juzar), [geekofshire](https://profiles.wordpress.org/geekofshire), [nazmulhassann20](https://profiles.wordpress.org/nazmulhassann20), [abhinavbelhekar03](https://profiles.wordpress.org/abhinavbelhekar03), [gautam23](https://profiles.wordpress.org/gautam23), [mukulsingh27](https://profiles.wordpress.org/mukulsingh27), [hbhalodia](https://profiles.wordpress.org/hbhalodia)
Tags: transcoder, video, media library, folders, file manager
@@ -11,7 +11,7 @@ Tested up to: 6.8.1
Requires PHP: 7.4
-Stable tag: 1.3.2
+Stable tag: 1.3.4
License: [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html)
diff --git a/admin/godam-transcoder-actions.php b/admin/godam-transcoder-actions.php
index 55dbeb42c..917aadfdf 100644
--- a/admin/godam-transcoder-actions.php
+++ b/admin/godam-transcoder-actions.php
@@ -58,18 +58,21 @@ function rtgodam_add_transcoded_url_field( $form_fields, $post ) {
'helps' => __( 'The URL of the transcoded file is generated automatically and cannot be edited.', 'godam' ),
);
- $form_fields['hls_transcoded_url'] = array(
- 'label' => __( 'Transcoded CDN URL (HLS)', 'godam' ),
- 'input' => 'html',
- 'html' => sprintf(
- '',
- (int) $post->ID,
- (int) $post->ID,
- esc_url( $hls_transcoded_url )
- ),
- 'value' => esc_url( $hls_transcoded_url ),
- 'helps' => __( 'The HLS URL of the transcoded file is generated automatically and cannot be edited.', 'godam' ),
- );
+ // Show the HLS transcoded URL field only for video files.
+ if ( strpos( $mime_type, 'video/' ) === 0 ) {
+ $form_fields['hls_transcoded_url'] = array(
+ 'label' => __( 'Transcoded CDN URL (HLS)', 'godam' ),
+ 'input' => 'html',
+ 'html' => sprintf(
+ '',
+ (int) $post->ID,
+ (int) $post->ID,
+ esc_url( $hls_transcoded_url )
+ ),
+ 'value' => esc_url( $hls_transcoded_url ),
+ 'helps' => __( 'The HLS URL of the transcoded file is generated automatically and cannot be edited.', 'godam' ),
+ );
+ }
} else {
// Display locked field with upsell message for free users.
$form_fields['transcoded_url'] = array(
diff --git a/assets/src/blocks/godam-player/components/VideoSEOModal.js b/assets/src/blocks/godam-player/components/VideoSEOModal.js
index f7a58f4a0..af00d3bc6 100644
--- a/assets/src/blocks/godam-player/components/VideoSEOModal.js
+++ b/assets/src/blocks/godam-player/components/VideoSEOModal.js
@@ -19,21 +19,32 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import './video-seo-modal.scss';
-import { getFirstNonEmpty, appendTimezoneOffsetToUTC, isObjectEmpty } from '../utils';
+import { isObjectEmpty } from '../utils';
-export default function VideoSEOModal( { isOpen, setIsOpen, attachmentData, attributes, setAttributes } ) {
+/**
+ * Video SEO Modal component
+ *
+ * @param {*} param0
+ * @param {boolean} param0.isOpen - Whether the modal is open
+ * @param {Function} param0.setIsOpen - Function to set modal open state
+ * @param {Object} param0.attributes - Block attributes
+ * @param {Function} param0.setAttributes - Function to set block attributes
+ *
+ * @return {JSX.Element|null} returns the Video SEO Modal component or null if not open
+ */
+export default function VideoSEOModal( { isOpen, setIsOpen, attributes, setAttributes } ) {
const [ videoData, setVideoData ] = useState( {} );
useEffect( () => {
- if ( attachmentData && ! isObjectEmpty( attachmentData ) ) {
+ if ( attributes.seo && ! isObjectEmpty( attributes.seo ) ) {
const initialVideoData = {
- contentUrl: getFirstNonEmpty( attributes?.seo?.contentUrl, attachmentData?.meta?.rtgodam_transcoded_url, attachmentData?.source_url ),
- headline: getFirstNonEmpty( attributes?.seo?.headline, attachmentData?.title?.rendered ),
- description: getFirstNonEmpty( attributes?.seo?.description, attachmentData?.description?.rendered ),
- uploadDate: getFirstNonEmpty( attributes?.seo?.uploadDate, appendTimezoneOffsetToUTC( attachmentData?.date_gmt ) ),
- duration: getFirstNonEmpty( attributes?.seo?.duration, attachmentData?.video_duration_iso8601 ),
- thumbnailUrl: getFirstNonEmpty( attributes?.seo?.thumbnailUrl, attachmentData?.meta?.rtgodam_media_video_thumbnail ),
- isFamilyFriendly: getFirstNonEmpty( attributes?.seo?.isFamilyFriendly, true ),
+ contentUrl: attributes?.seo?.contentUrl || '',
+ headline: attributes?.seo?.headline || '',
+ description: attributes?.seo?.description || '',
+ uploadDate: attributes?.seo?.uploadDate || '',
+ duration: attributes?.seo?.duration || '',
+ thumbnailUrl: attributes?.seo?.thumbnailUrl || '',
+ isFamilyFriendly: attributes?.seo?.isFamilyFriendly || true,
};
setVideoData( initialVideoData );
@@ -46,7 +57,7 @@ export default function VideoSEOModal( { isOpen, setIsOpen, attachmentData, attr
} );
}
}
- }, [ attachmentData, attributes, setAttributes ] ); // Remove isOpen from dependencies
+ }, [ attributes, setAttributes ] ); // Remove isOpen from dependencies
const updateField = ( field, value ) => {
setVideoData( { ...videoData, [ field ]: value } );
diff --git a/assets/src/blocks/godam-player/edit.js b/assets/src/blocks/godam-player/edit.js
index 8ed095003..6dbb56cd4 100644
--- a/assets/src/blocks/godam-player/edit.js
+++ b/assets/src/blocks/godam-player/edit.js
@@ -43,6 +43,7 @@ import Video from './VideoJS';
import TracksEditor from './track-uploader';
import { Caption } from './caption';
import VideoSEOModal from './components/VideoSEOModal.js';
+import { appendTimezoneOffsetToUTC, secondsToISO8601 } from './utils/index.js';
const ALLOWED_MEDIA_TYPES = [ 'video' ];
const VIDEO_POSTER_ALLOWED_MEDIA_TYPES = [ 'image' ];
@@ -106,7 +107,6 @@ function VideoEdit( {
const [ temporaryURL, setTemporaryURL ] = useState( attributes.blob );
const [ defaultPoster, setDefaultPoster ] = useState( '' );
const [ isSEOModalOpen, setIsSEOModelOpen ] = useState( false );
- const [ videoResponse, setVideoResponse ] = useState( {} );
const [ duration, setDuration ] = useState( 0 );
const dispatch = useDispatch();
@@ -167,8 +167,6 @@ function VideoEdit( {
try {
const response = await apiFetch( { path: `/wp/v2/media/${ id }` } );
- setVideoResponse( response );
-
if ( response.meta.rtgodam_media_video_thumbnail !== '' ) {
setDefaultPoster( response.meta.rtgodam_media_video_thumbnail );
}
@@ -252,54 +250,87 @@ function VideoEdit( {
setDefaultPoster( media.image?.src );
}
+ if ( media?.origin === 'godam' ) {
+ setAttributes( {
+ seo: {
+ contentUrl: media?.url,
+ headline: media?.title || '',
+ description: media?.description || '',
+ uploadDate: appendTimezoneOffsetToUTC( media?.date || '' ),
+ duration: secondsToISO8601( media?.duration || '' ),
+ thumbnailUrl: media?.thumbnail_url || '',
+ isFamilyFriendly: true, // Default value
+ },
+ } );
+
+ setAttributes( {
+ sources: [
+ {
+ src: media.url,
+ type: media.url.endsWith( '.mov' ) ? 'video/mp4' : media.mime,
+ },
+ ],
+ } );
+ } else {
// Fetch transcoded URL from media meta.
- ( async () => {
- try {
- const response = await apiFetch( { path: `/wp/v2/media/${ media.id }` } );
+ ( async () => {
+ try {
+ const response = await apiFetch( { path: `/wp/v2/media/${ media.id }` } );
- setVideoResponse( response );
+ setAttributes( {
+ seo: {
+ contentUrl: response.meta?.rtgodam_transcoded_url || response.source_url,
+ headline: response.title?.rendered || '',
+ description: response.description?.rendered || '',
+ uploadDate: appendTimezoneOffsetToUTC( response.date_gmt ),
+ duration: response.video_duration_iso8601 || '',
+ thumbnailUrl: response.meta?.rtgodam_media_video_thumbnail || '',
+ isFamilyFriendly: true, // Default value
+ },
+ } );
- if ( response && response.meta && response.meta.rtgodam_transcoded_url ) {
- const transcodedUrl = response.meta.rtgodam_transcoded_url;
+ if ( response && response.meta && response.meta.rtgodam_transcoded_url ) {
+ const transcodedUrl = response.meta.rtgodam_transcoded_url;
- if ( response.meta.rtgodam_media_video_thumbnail !== '' ) {
- setDefaultPoster( response.meta.rtgodam_media_video_thumbnail );
- }
+ if ( response.meta.rtgodam_media_video_thumbnail !== '' ) {
+ setDefaultPoster( response.meta.rtgodam_media_video_thumbnail );
+ }
- setAttributes( {
- sources: [
- {
- src: transcodedUrl,
- type: transcodedUrl.endsWith( '.mpd' ) ? 'application/dash+xml' : media.mime,
- },
- {
- src: media.url,
- type: media.url.endsWith( '.mov' ) ? 'video/mp4' : media.mime,
- },
- ],
- } );
- } else {
+ setAttributes( {
+ sources: [
+ {
+ src: transcodedUrl,
+ type: transcodedUrl.endsWith( '.mpd' ) ? 'application/dash+xml' : media.mime,
+ },
+ {
+ src: media.url,
+ type: media.url.endsWith( '.mov' ) ? 'video/mp4' : media.mime,
+ },
+ ],
+ } );
+ } else {
// If meta not present, use media url.
+ setAttributes( {
+ sources: [
+ {
+ src: media.url,
+ type: media.url.endsWith( '.mov' ) ? 'video/mp4' : media.mime,
+ },
+ ],
+ } );
+ }
+ } catch ( error ) {
setAttributes( {
sources: [
{
src: media.url,
- type: media.url.endsWith( '.mov' ) ? 'video/mp4' : media.mime,
+ type: media.mime,
},
],
} );
}
- } catch ( error ) {
- setAttributes( {
- sources: [
- {
- src: media.url,
- type: media.mime,
- },
- ],
- } );
- }
- } )();
+ } )();
+ }
setTemporaryURL();
}
@@ -604,7 +635,6 @@ function VideoEdit( {
diff --git a/assets/src/blocks/godam-player/utils/index.js b/assets/src/blocks/godam-player/utils/index.js
index 79a8ad1b5..fa2bb5a3f 100644
--- a/assets/src/blocks/godam-player/utils/index.js
+++ b/assets/src/blocks/godam-player/utils/index.js
@@ -63,6 +63,8 @@ function isObjectEmpty( obj ) {
* @return {string} An ISO 8601 duration string in the format 'PTnHnMnS'.
*/
function secondsToISO8601( seconds ) {
+ seconds = Math.floor( seconds ); // ensure whole seconds
+
const duration = {
hours: Math.floor( seconds / 3600 ),
minutes: Math.floor( ( seconds % 3600 ) / 60 ),
diff --git a/assets/src/css/_variables.scss b/assets/src/css/_variables.scss
index 2b0990bed..5ab2c208d 100644
--- a/assets/src/css/_variables.scss
+++ b/assets/src/css/_variables.scss
@@ -1,6 +1,7 @@
$godam-primary-gradient: linear-gradient(83.85deg, #AB3A6C -9.3%, #E6533A 120.31%);
$godam-primary-color: rgba(194, 0, 0, 1);
$godam-light-color: rgba(255, 244, 244, 1);
+$godam-primary-text-color: #AB3A6C;
$godam-gray-color: rgba(243, 245, 247, 1);
$godam-black-color: rgba(33, 33, 33, 1);
diff --git a/assets/src/css/admin.scss b/assets/src/css/admin.scss
index 8513e93c9..6cd9d6d27 100644
--- a/assets/src/css/admin.scss
+++ b/assets/src/css/admin.scss
@@ -1,4 +1,6 @@
+@use './variables';
+
@import url(https://vjs.zencdn.net/8.3.0/video-js.css);
@import "_common"; // stylelint-disable-line scss/load-no-partial-leading-underscore
@@ -11,6 +13,9 @@ $LIGHT_BLUE: #2196F3;
--wp-components-color-accent: #ab3a6c;
}
+#wpbody-content {
+ container: wpBodyContent / inline-size;
+}
/*
Write your Sass code here for admin.
@@ -581,3 +586,149 @@ $media-frame-width: calc(300px + 2 * 24px);
color: #ab3a6c;
}
}
+
+/*
+Video Editor and Media Library's Annual plan offer banner styles
+
+@todo: Remove container queries when the media-library static sidebar is converted to an overlay sidebar
+to ensure proper responsiveness and avoid layout issues.
+*/
+.annual-plan-offer-banner {
+ border: none;
+ margin: 0;
+ margin-left: -2rem;
+ background: variables.$godam-primary-gradient;
+ display: flex;
+ justify-content: center;
+ position: relative;
+ margin-bottom: 0 !important;
+
+ #godam-particle-canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+ pointer-events: none;
+ }
+
+ &__content {
+ position: relative;
+ z-index: 1;
+ padding: 24px;
+ width: 100%;
+ max-width: 1024px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 2rem;
+ margin-right: 30px;
+
+ @media (max-width: 782px) {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 18px;
+ padding-left: 0;
+ }
+
+ @container wpBodyContent (max-width: 640px) {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 18px;
+ padding-left: 0;
+ }
+ }
+
+ &__title {
+ margin: 0;
+ font-size: 1.5rem;
+ color: #fff;
+ font-weight: 700;
+ line-height: 1.2;
+ letter-spacing: 0.5px;
+
+ @media (max-width: 782px) {
+ font-size: 1.25rem;
+ }
+
+ @container wpBodyContent (max-width: 640px) {
+ font-size: 1.25rem;
+ }
+ }
+
+ &__description {
+ margin: 10px 0 0 !important;
+ padding: 0 !important;
+ font-size: 1rem;
+ color: #ececec;
+ line-height: 1.2;
+ font-weight: 200;
+ letter-spacing: 0.3px;
+
+ @media (max-width: 782px) {
+ font-size: 0.875rem;
+ margin-top: 8px !important;
+ }
+
+ @container wpBodyContent (max-width: 640px) {
+ font-size: 0.875rem;
+ margin-top: 8px !important;
+ }
+ }
+
+ &__cta-container {
+ display: flex;
+ align-items: center;
+ }
+
+ &__cta {
+ background-color: #fff;
+ color: variables.$godam-primary-text-color;
+ padding: 10px 16px;
+ border-radius: 8px;
+ font-weight: 600;
+ text-decoration: none;
+ white-space: nowrap;
+ text-transform: uppercase;
+ box-shadow: 0 0 50px 2px rgba(255, 255, 255, 1);
+
+ @media (max-width: 782px) {
+ padding: 6px 12px;
+ box-shadow: 0 0 50px 0 rgba(255, 255, 255, 1);
+ }
+
+ @container wpBodyContent (max-width: 640px) {
+ padding: 6px 12px;
+ box-shadow: 0 0 50px 0 rgba(255, 255, 255, 1);
+ }
+
+ &:hover {
+ color: variables.$godam-primary-text-color;
+ }
+ }
+
+ &__dismiss {
+ position: absolute;
+ top: 8px;
+ right: 10px;
+ background: none;
+ border: none;
+ color: #fff;
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0.1rem .5rem 0.3rem .5rem;
+ line-height: 1;
+ z-index: 10;
+ transition: all 0.3s ease;
+ margin-left: 16px;
+
+ &:hover {
+ background-color: #fff;
+ color: variables.$godam-primary-text-color;
+ border-radius: 4px;
+ }
+ }
+}
diff --git a/assets/src/css/media-library.scss b/assets/src/css/media-library.scss
index 5b71bea38..097fdb7a5 100644
--- a/assets/src/css/media-library.scss
+++ b/assets/src/css/media-library.scss
@@ -332,33 +332,3 @@
.elementor-editor-active .attachments-browser #media-date-range-filter {
background-color: unset;
}
-
-.annual-plan-offer-banner {
- border: none;
- margin: 0;
- margin-left: -2rem;
- background: linear-gradient(90deg, #AB3A6C, #E6533A);
- display: flex;
- justify-content: center;
- position: relative;
-
- &__img {
- width: 100%;
- height: auto;
- max-width: 1000px;
- }
-
- &__dismiss {
- position: absolute;
- top: 0;
- right: 0;
- background: none;
- border: none;
- color: #fff;
- font-size: 1.5rem;
- cursor: pointer;
- padding: 0.25rem 0.5rem;
- line-height: 1;
- z-index: 10;
- }
-}
diff --git a/assets/src/js/admin.js b/assets/src/js/admin.js
index 9e771d2b7..d7ea7f8aa 100644
--- a/assets/src/js/admin.js
+++ b/assets/src/js/admin.js
@@ -2,6 +2,7 @@
* Write your JS code here for admin.
*/
+// Utility function to join URL paths
window.pathJoin = function( parts, sep = '/' ) {
return parts
.map( ( part, index ) => {
@@ -14,7 +15,10 @@ window.pathJoin = function( parts, sep = '/' ) {
.join( sep );
};
-document.addEventListener( 'DOMContentLoaded', function() {
+/**
+ * Toggle postboxes in admin UI
+ */
+function initTogglePostboxes() {
const toggleButtons = document.querySelectorAll( '#easydam-tools-widget .handlediv' );
toggleButtons.forEach( ( button ) => {
@@ -29,4 +33,112 @@ document.addEventListener( 'DOMContentLoaded', function() {
}
} );
} );
-} );
+}
+
+/**
+ * Particle class for canvas-based particle effect
+ */
+class Particle {
+ constructor( ctx, width, height ) {
+ this.ctx = ctx;
+ this.width = width;
+ this.height = height;
+ this.x = Math.random() * width;
+ this.y = Math.random() * height;
+ this.radius = ( Math.random() * 1.5 ) + 1;
+ this.vx = ( Math.random() * 0.5 ) - 0.25;
+ this.vy = ( Math.random() * 0.5 ) - 0.25;
+ }
+
+ // Draw the particle
+ draw() {
+ this.ctx.beginPath();
+ this.ctx.arc( this.x, this.y, this.radius, 0, Math.PI * 2 );
+ this.ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
+ this.ctx.fill();
+ }
+
+ // Update the particle position
+ update() {
+ this.x += this.vx;
+ this.y += this.vy;
+
+ if ( this.x < 0 || this.x > this.width ) {
+ this.vx *= -1;
+ }
+ if ( this.y < 0 || this.y > this.height ) {
+ this.vy *= -1;
+ }
+ }
+}
+
+/**
+ * Manages the particle effect background
+ */
+class ParticleEffect {
+ constructor() {
+ // Initialize canvas and context
+ this.canvas = document.getElementById( 'godam-particle-canvas' );
+ this.banner = document.querySelector( '.annual-plan-offer-banner' );
+
+ // Bail if canvas and banner do not exist
+ if ( ! this.canvas || ! this.banner ) {
+ return;
+ }
+
+ this.ctx = this.canvas.getContext( '2d' );
+ this.particles = [];
+ this.setupEventListeners();
+ this.resizeCanvas();
+ this.animate();
+ }
+
+ // Set up event listeners for resizing the canvas
+ setupEventListeners() {
+ window.addEventListener( 'resize', this.debounce( () => this.resizeCanvas(), 100 ) );
+ }
+
+ // Debounce function to limit the rate of function execution
+ debounce( fn, delay ) {
+ let timer;
+ return function() {
+ clearTimeout( timer );
+ timer = setTimeout( fn, delay );
+ };
+ }
+
+ // Resize the canvas to fit the banner
+ resizeCanvas() {
+ this.width = this.canvas.width = this.banner.offsetWidth || window.innerWidth;
+ this.height = this.canvas.height = this.banner.offsetHeight || 300;
+ this.initParticles( 40 );
+ }
+
+ // Initialize particles based on the canvas size
+ initParticles( count ) {
+ const maxParticles = Math.min( count, Math.floor( this.width / 20 ) );
+ this.particles = Array.from( { length: maxParticles }, () => new Particle( this.ctx, this.width, this.height ) );
+ }
+
+ // Start the animation loop
+ animate() {
+ // Bail if canvas and context do not exist
+ if ( ! this.canvas || ! this.ctx ) {
+ return;
+ }
+
+ this.ctx.clearRect( 0, 0, this.width, this.height );
+ this.particles.forEach( ( p ) => {
+ p.update();
+ p.draw();
+ } );
+ requestAnimationFrame( () => this.animate() );
+ }
+}
+
+function initAdminUI() {
+ initTogglePostboxes();
+ new ParticleEffect();
+}
+
+document.addEventListener( 'DOMContentLoaded', initAdminUI );
diff --git a/assets/src/js/godam-player/masterSettings.js b/assets/src/js/godam-player/masterSettings.js
index f4a778b55..8b3202ad8 100644
--- a/assets/src/js/godam-player/masterSettings.js
+++ b/assets/src/js/godam-player/masterSettings.js
@@ -38,6 +38,8 @@ class SettingsButton extends videojs.getComponent( 'MenuButton' ) {
constructor( player, options ) {
super( player, options );
this.player_ = player;
+ this.player_.selectedSpeed ??= '1x';
+ this.player_.selectedQuality ??= 'Auto';
this.addClass( 'vjs-settings-button' );
this.controlText( 'Settings' );
this.hasQualityItem = false;
@@ -78,6 +80,33 @@ class SettingsButton extends videojs.getComponent( 'MenuButton' ) {
super.handleClick( event );
}
+ // Method for the outside click listener
+ outsideClickListener() {
+ if ( this.el() ) {
+ this.menu.unlockShowing();
+ this.resetToDefaultMenu();
+ }
+ }
+
+ // Method to add an event listener to close the settings menu when clicked outside
+ attachOutsideClickListener() {
+ if ( ! this.el() ) {
+ return;
+ }
+
+ // Attach a new outside click listener and store its reference
+ this._outsideClickListener = this.outsideClickListener.bind( this );
+ document.addEventListener( 'click', this._outsideClickListener, { once: true } );
+ }
+
+ // Method to detach the outside click listener
+ detachOutsideClickListener() {
+ if ( this._outsideClickListener ) {
+ document.removeEventListener( 'click', this._outsideClickListener );
+ this._outsideClickListener = null;
+ }
+ }
+
// Method to reset menu to default state
resetToDefaultMenu() {
if ( this.originalItems_ ) {
@@ -99,10 +128,21 @@ class SettingsButton extends videojs.getComponent( 'MenuButton' ) {
this.originalItems_ = null;
}
}
+
+ // Override the superclass dispose method
+ dispose() {
+ this.detachOutsideClickListener(); // Detach the outside click listener
+ super.dispose();
+ }
+}
+
+// Utility function to close menu and reset to default menu
+function closeAndResetMenu( menuButton ) {
+ menuButton.menu.hide();
+ menuButton.menu.unlockShowing();
+ menuButton.resetToDefaultMenu(); // reset to default menu
}
-let selectedSpeed = '1x';
-let selectedQuality = 'Auto'; // initiated globally so it can be share between multiple instances of the function.
function openSubmenu( menuButton, items, title = '' ) {
// Ensure the menu is created
if ( ! menuButton.menu ) {
@@ -163,9 +203,11 @@ function openSubmenu( menuButton, items, title = '' ) {
let isSelected = false;
if ( title === 'Speed' ) {
- isSelected = selectedSpeed === itemData.label;
+ const currentSpeed = this.player().selectedSpeed || '1x';
+ isSelected = currentSpeed === itemData.label;
} else if ( title === 'Quality' ) {
- isSelected = selectedQuality === itemData.label;
+ const currentQuality = this.player().selectedQuality || 'Auto';
+ isSelected = currentQuality === itemData.label;
}
let html = '';
@@ -213,7 +255,7 @@ function openSubmenu( menuButton, items, title = '' ) {
for ( let i = 0; i < qualityLevels.length; i++ ) {
qualityLevels[ i ].enabled = true;
}
- selectedQuality = 'Auto';
+ this.player().selectedQuality = 'Auto';
} else {
// Parse height from label like '720p'
const selectedHeight = parseInt( qualityLabel.replace( 'p', '' ), 10 );
@@ -222,34 +264,19 @@ function openSubmenu( menuButton, items, title = '' ) {
const level = qualityLevels[ i ];
level.enabled = level.height === selectedHeight;
}
- selectedQuality = selectedHeight + 'p';
+ this.player().selectedQuality = selectedHeight + 'p';
}
- menuButton.el_.focus(); // keep menu focused
- openSubmenu( menuButton, items, title ); // refresh menu to update ticks
+ closeAndResetMenu( menuButton );
}
handleSpeedSelection( speed ) {
// Implement playback speed change
const rate = parseFloat( speed.replace( 'x', '' ) );
this.player().playbackRate( rate );
- selectedSpeed = speed;
-
- document
- .querySelectorAll( '.easydam-player.video-js' )
- .forEach( ( videoElement ) => {
- try {
- const player = videojs.getPlayer( videoElement );
- if ( player ) {
- player.playbackRate( rate );
- }
- } catch ( error ) {
- // silently fail.
- }
- } );
+ this.player().selectedSpeed = speed;
- menuButton.el_.focus();
- openSubmenu( menuButton, items, title ); // refresh menu to show new tick
+ closeAndResetMenu( menuButton );
}
}
@@ -265,6 +292,11 @@ function openSubmenu( menuButton, items, title = '' ) {
// Force menu to update
menuButton.menu.show();
+
+ // Ensure the outside click listener is attached
+ if ( menuButton && typeof menuButton.attachOutsideClickListener === 'function' ) {
+ menuButton.attachOutsideClickListener();
+ }
}
class QualityMenuItem extends videojs.getComponent( 'MenuItem' ) {
diff --git a/godam.php b/godam.php
index 6c4c50239..8d0cfbb75 100644
--- a/godam.php
+++ b/godam.php
@@ -3,7 +3,7 @@
* Plugin Name: GoDAM
* Plugin URI: https://godam.io
* Description: Seamlessly manage and deliver your media assets directly from the cloud-based media management. Store assets efficiently, stream them via a CDN, and enhance your website's performance and user experience. Featuring adaptive bit rate streaming, adding interactive layers in videos, and taking full advantage of a digital asset management solution within WordPress.
- * Version: 1.3.2
+ * Version: 1.3.4
* Requires at least: 6.5
* Requires PHP: 7.4
* Text Domain: godam
@@ -43,7 +43,7 @@
/**
* The version of the plugin
*/
- define( 'RTGODAM_VERSION', '1.3.2' );
+ define( 'RTGODAM_VERSION', '1.3.4' );
}
if ( ! defined( 'RTGODAM_NO_MAIL' ) && defined( 'VIP_GO_APP_ENVIRONMENT' ) ) {
diff --git a/inc/classes/class-media-library-ajax.php b/inc/classes/class-media-library-ajax.php
index 97dc8fbfd..4be0d635c 100644
--- a/inc/classes/class-media-library-ajax.php
+++ b/inc/classes/class-media-library-ajax.php
@@ -32,7 +32,6 @@ protected function __construct() {
*/
public function setup_hooks() {
add_filter( 'ajax_query_attachments_args', array( $this, 'filter_media_library_by_taxonomy' ) );
- add_filter( 'ajax_query_attachments_args', array( $this, 'godam_media_library_ajax' ) );
add_action( 'pre_get_posts', array( $this, 'pre_get_post_filter' ) );
add_action( 'restrict_manage_posts', array( $this, 'restrict_manage_media_filter' ) );
@@ -47,103 +46,6 @@ public function setup_hooks() {
add_action( 'wp_ajax_godam_dismiss_offer_banner', array( $this, 'dismiss_offer_banner' ) );
}
- /**
- * Short-circuit the media library AJAX request if the mime type is 'godam'.
- *
- * @param array $query_args Query arguments.
- * @return array
- */
- public function godam_media_library_ajax( $query_args ) {
-
- $api_key = get_option( 'rtgodam-api-key', '' );
-
- if ( empty( $api_key ) ) {
- return $query_args;
- }
-
- if ( isset( $query_args['post_mime_type'] ) && is_array( $query_args['post_mime_type'] ) ) {
-
- $post_mime_type = $query_args['post_mime_type'][0];
- $mime_type = '';
- if ( false === strpos( $post_mime_type, 'godam/' ) ) {
- return $query_args;
- } else {
- // mime_type is godam/{mime_type}.
- $mime_type = str_replace( 'godam/', '', $post_mime_type );
- $mime_type = explode( '-', $mime_type );
- $mime_type = $mime_type[0];
- if ( 'all' === $mime_type ) {
- $mime_type = '';
- }
- }
-
- $api_url = RTGODAM_API_BASE . '/api/method/godam_core.api.file.get_list_of_files_with_api_key';
-
-
- $order_by = 'creation asc';
- if ( isset( $query_args['order'] ) && 'DESC' === $query_args['order'] ) {
- $order_by = 'creation desc';
- }
-
- $request_args = array(
- 'api_key' => $api_key,
- 'order_by' => $order_by,
- );
-
- if ( ! empty( $mime_type ) ) {
- if ( 'video' === $mime_type ) {
- $request_args['job_type'] = 'stream';
- } else {
- $request_args['job_type'] = $mime_type;
- }
- }
-
- if ( isset( $query_args['s'] ) && ! empty( $query_args['s'] ) ) {
- $request_args['search'] = $query_args['s'];
- }
-
- if ( isset( $query_args['posts_per_page'] ) && ! empty( $query_args['paged'] ) ) {
- $request_args['page_size'] = intval( $query_args['posts_per_page'] );
- $request_args['page'] = intval( $query_args['paged'] );
- }
-
- $api_url = add_query_arg(
- $request_args,
- $api_url
- );
-
- $response = wp_remote_get(
- $api_url,
- array(
- 'headers' => array(
- 'Content-Type' => 'application/json',
- ),
- )
- );
-
- if ( is_wp_error( $response ) ) {
- return $query_args;
- }
-
- $body = json_decode( wp_remote_retrieve_body( $response ) );
-
- if ( ! is_object( $body ) || ! isset( $body->message ) || ! isset( $body->message->files ) ) {
- return $query_args;
- }
-
- $response = $body->message->files;
-
- foreach ( $response as $key => $item ) {
- $response[ $key ] = $this->prepare_godam_media_item( $item );
- }
-
- wp_send_json_success( $response );
-
- } else {
- return $query_args;
- }
- }
-
/**
* Prepare a single Godam media item to match WordPress media format.
*
@@ -160,19 +62,22 @@ public function prepare_godam_media_item( $item ) {
$result = array(
'id' => $item['name'],
- 'title' => pathinfo( $item['orignal_file_name'], PATHINFO_FILENAME ) ?? $item['name'],
+ 'title' => isset( $item['orignal_file_name'] ) ? pathinfo( $item['orignal_file_name'], PATHINFO_FILENAME ) : $item['name'],
'filename' => $item['orignal_file_name'] ?? $item['name'],
- 'url' => 'image' === $item['job_type'] ? $item['file_origin'] : ( $item['transcoded_file_path'] ?? $item['file_origin'] ),
- 'mime' => 'application/dash+xml' ?? '',
+ 'url' => ( $item['job_type'] ?? '' ) === 'image' ? ( $item['file_origin'] ?? '' ) : ( $item['transcoded_file_path'] ?? $item['file_origin'] ?? '' ),
+ 'mime' => 'application/dash+xml',
'type' => $item['job_type'] ?? '',
- 'subtype' => explode( '/', $item['mime_type'] )[1] ?? 'jpg',
- 'status' => $item['status'],
- 'date' => strtotime( $item['creation'] ) * 1000,
- 'modified' => strtotime( $item['modified'] ) * 1000,
- 'filesizeInBytes' => $item['file_size'],
- 'filesizeHumanReadable' => size_format( $item['file_size'] ),
+ 'subtype' => ( isset( $item['mime_type'] ) && strpos( $item['mime_type'], '/' ) !== false ) ? explode( '/', $item['mime_type'] )[1] : 'jpg',
+ 'status' => $item['status'] ?? '',
+ 'date' => isset( $item['creation'] ) ? strtotime( $item['creation'] ) * 1000 : 0,
+ 'modified' => isset( $item['modified'] ) ? strtotime( $item['modified'] ) * 1000 : 0,
+ 'filesizeInBytes' => $item['file_size'] ?? 0,
+ 'filesizeHumanReadable' => isset( $item['file_size'] ) ? size_format( $item['file_size'] ) : '',
'owner' => $item['owner'] ?? '',
'label' => $item['file_label'] ?? '',
+ 'origin' => 'godam',
+ 'thumbnail_url' => $item['thumbnail_url'] ?? '',
+ 'duration' => $item['playtime'] ?? '',
);
if ( 'stream' === $item['job_type'] ) {
@@ -636,28 +541,52 @@ public function media_library_offer_banner() {
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$banner_html = sprintf(
- '
-
-
-
-
+ '
',
- esc_url( 'https://godam.io/pricing?utm_campaign=annual-plan&utm_source=' . $host . '&utm_medium=plugin&utm_content=banner' ),
- esc_url( RTGODAM_URL . '/assets/src/images/annual-plan-offer-banner.png' )
+ esc_html__( 'Pay for 10 months and get 2 months free with our annual plan.', 'godam' ),
+ esc_html__( 'Elevate your media management, transcoding, storage, delivery and more.', 'godam' ),
+ esc_url( RTGODAM_IO_API_BASE . '/pricing?utm_campaign=annual-plan&utm_source=' . $host . '&utm_medium=plugin&utm_content=banner' ),
+ esc_html__( 'Buy Now', 'godam' )
);
echo wp_kses(
$banner_html,
array(
'div' => array( 'class' => array() ),
+ 'canvas' => array( 'id' => array() ),
+ 'h3' => array( 'class' => array() ),
+ 'p' => array( 'class' => array() ),
'a' => array(
- 'href' => array(),
- 'class' => array(),
- ),
- 'img' => array(
- 'src' => array(),
- 'class' => array(),
- 'alt' => array(),
+ 'href' => array(),
+ 'class' => array(),
+ 'target' => array(),
+ 'rel' => array(),
+ 'title' => array(),
),
'button' => array(
'type' => array(),
@@ -665,7 +594,6 @@ public function media_library_offer_banner() {
),
)
);
-
}
}
}
diff --git a/inc/classes/class-pages.php b/inc/classes/class-pages.php
index f10e2ad5b..67f5f6f5d 100644
--- a/inc/classes/class-pages.php
+++ b/inc/classes/class-pages.php
@@ -366,6 +366,7 @@ public function admin_enqueue_scripts() {
'currentUserRoles' => wp_get_current_user()->roles, // Current user roles.
'validApiKey' => rtgodam_is_api_key_valid(),
'adminUrl' => admin_url(),
+ 'godamBaseUrl' => RTGODAM_IO_API_BASE,
'gfActive' => $is_gf_active,
'cf7Active' => $is_cf7_active,
'wpformsActive' => $is_wpforms_active,
diff --git a/inc/classes/class-seo.php b/inc/classes/class-seo.php
index 7be179070..a265cfe1c 100644
--- a/inc/classes/class-seo.php
+++ b/inc/classes/class-seo.php
@@ -105,7 +105,7 @@ private function extract_video_seo_schema_from_block( $block ) {
$schemas[] = $block['attrs']['seo'];
}
}
-
+
if ( ! empty( $block['innerBlocks'] ) ) {
foreach ( $block['innerBlocks'] as $inner_block ) {
$schemas = array_merge(
@@ -114,7 +114,7 @@ private function extract_video_seo_schema_from_block( $block ) {
);
}
}
-
+
return $schemas;
}
@@ -128,14 +128,14 @@ private function extract_video_seo_schema_from_block( $block ) {
public function add_video_duration_for_video_seo( $response, $post ) {
if ( 'video' === $post->post_mime_type || str_starts_with( $post->post_mime_type, 'video/' ) ) {
$meta = wp_get_attachment_metadata( $post->ID );
-
+
if ( ! empty( $meta['length'] ) && is_numeric( $meta['length'] ) ) {
$duration_seconds = (int) $meta['length'];
-
+
$hours = floor( $duration_seconds / 3600 );
$minutes = floor( ( $duration_seconds % 3600 ) / 60 );
$seconds = $duration_seconds % 60;
-
+
$iso_duration = 'PT';
if ( $hours > 0 ) {
$iso_duration .= $hours . 'H';
@@ -146,11 +146,11 @@ public function add_video_duration_for_video_seo( $response, $post ) {
if ( $seconds > 0 || 'PT' === $iso_duration ) {
$iso_duration .= $seconds . 'S';
}
-
+
$response->data['video_duration_iso8601'] = $iso_duration;
}
}
-
+
return $response;
}
@@ -172,31 +172,31 @@ public function add_video_seo_schema() {
if ( ! is_singular() ) {
return;
}
-
+
global $post;
-
+
$raw = get_post_meta( $post->ID, self::VIDEO_SEO_SCHEMA_META_KEY, true );
-
+
// Normalize and validate raw data.
if ( empty( $raw ) ) {
return;
}
-
+
if ( ! is_array( $raw ) ) {
$raw = maybe_unserialize( $raw );
}
-
+
if ( ! is_array( $raw ) || empty( $raw ) ) {
return;
}
-
+
$schemas = array();
-
+
foreach ( $raw as $video ) {
if ( ! is_array( $video ) ) {
continue;
}
-
+
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'VideoObject',
@@ -206,22 +206,22 @@ public function add_video_seo_schema() {
'uploadDate' => sanitize_text_field( $video['uploadDate'] ?? '' ),
'isFamilyFriendly' => isset( $video['isFamilyFriendly'] ) ? (bool) $video['isFamilyFriendly'] : true,
);
-
+
if ( ! empty( $video['thumbnailUrl'] ) ) {
$schema['thumbnailUrl'] = esc_url_raw( $video['thumbnailUrl'] );
}
-
+
if ( ! empty( $video['duration'] ) ) {
$schema['duration'] = sanitize_text_field( $video['duration'] );
}
-
+
$schemas[] = $schema;
}
if ( empty( $schemas ) ) {
return;
}
-
+
// Output a single