$attributes Player attributes.
* @param string $context Rendering context.
* @return string
*/
public static function render_player( $attributes, $context = 'frontend' ) {
self::enqueue_block_assets();
$settings = Settings::get();
$args = self::normalize_attributes( $attributes, $settings );
$theme_data = self::get_theme_presets();
$theme_name = isset( $theme_data[ $args['theme'] ] ) ? $theme_data[ $args['theme'] ]['label'] : $theme_data[ Settings::get_default_preset_slug() ]['label'];
$design_settings = Settings::get_resolved_design_settings( $args['theme'], $settings );
$style_attr = Settings::build_design_css_variables( $design_settings );
$is_editor_context = self::is_editor_context( $context );
$playlist_payload = self::prepare_playlist_payload( $args['playlist'] );
$current_track = $playlist_payload[ $args['currentIndex'] ];
$show_cover_image = ! empty( $current_track['image'] ) && ! empty( $design_settings['player_show_cover_image'] );
$instance_id = 'map-player-' . wp_unique_id();
if ( empty( $current_track['src'] ) ) {
if ( $is_editor_context ) {
return self::render_placeholder( $args['theme'], $theme_name, $style_attr, $design_settings );
}
return '';
}
ob_start();
?>
>
>
*/
public static function get_theme_presets() {
return Settings::get_design_presets();
}
/**
* Normalize and sanitize player attributes.
*
* @param array $attributes Raw attributes.
* @param array $settings Global settings.
* @return array
*/
public static function normalize_attributes( $attributes, $settings = array() ) {
if ( empty( $settings ) ) {
$settings = Settings::get();
}
$defaults = array(
'src' => '',
'title' => __( 'Untitled Track', 'modern-audio-player' ),
'image' => '',
'playlist' => array(),
'theme' => $settings['default_theme'],
'useGlobalTheme' => true,
);
$args = wp_parse_args( is_array( $attributes ) ? $attributes : array(), $defaults );
$args['src'] = esc_url_raw( $args['src'] );
$args['title'] = sanitize_text_field( $args['title'] );
$args['image'] = esc_url_raw( $args['image'] );
$use_global_theme = rest_sanitize_boolean( $args['useGlobalTheme'] );
$enabled_themes = isset( $settings['enabled_themes'] ) && is_array( $settings['enabled_themes'] ) ? $settings['enabled_themes'] : array_keys( self::get_theme_presets() );
$requested_theme = sanitize_key( $args['theme'] );
if ( $use_global_theme || ! in_array( $requested_theme, $enabled_themes, true ) ) {
$args['theme'] = $settings['default_theme'];
} else {
$args['theme'] = $requested_theme;
}
$args['playlist'] = self::normalize_playlist_items( $args['playlist'], $args );
if ( empty( $args['playlist'] ) ) {
$args['playlist'] = array(
self::prepare_track(
array(
'src' => $args['src'],
'title' => $args['title'],
'image' => $args['image'],
)
),
);
}
$args['currentIndex'] = 0;
$args['src'] = $args['playlist'][0]['src'];
$args['title'] = $args['playlist'][0]['title'];
$args['image'] = $args['playlist'][0]['image'];
$args['useGlobalTheme'] = $use_global_theme;
return $args;
}
/**
* Detect whether the current render is happening inside the block editor.
*
* @param string $context Rendering context.
* @return bool
*/
private static function is_editor_context( $context ) {
if ( 'editor' === $context || is_admin() ) {
return true;
}
if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
return false;
}
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
$rest_context = isset( $_REQUEST['context'] ) ? sanitize_key( wp_unslash( $_REQUEST['context'] ) ) : '';
$referer = wp_get_referer();
if ( 'edit' === $rest_context || false !== strpos( $request_uri, '/wp/v2/block-renderer/' ) ) {
return true;
}
return is_string( $referer ) && false !== strpos( $referer, 'post.php' );
}
/**
* Render a visible placeholder for editor previews without an audio source.
*
* @param string $theme_slug Theme class suffix.
* @param string $theme_name Human-readable theme name.
* @param string $style_attr Inline CSS variable string.
* @param array $design_settings Resolved design settings.
* @return string
*/
private static function render_placeholder( $theme_slug, $theme_name, $style_attr, $design_settings ) {
ob_start();
?>
$args Normalized player arguments.
* @return array>
*/
private static function normalize_playlist_items( $playlist, $args ) {
$normalized = array();
if ( is_array( $playlist ) ) {
foreach ( $playlist as $track ) {
$prepared = self::prepare_track( is_array( $track ) ? $track : array() );
if ( '' !== $prepared['src'] ) {
$normalized[] = $prepared;
}
}
}
if ( empty( $normalized ) && ! empty( $args['src'] ) ) {
$normalized[] = self::prepare_track(
array(
'src' => $args['src'],
'title' => $args['title'],
'image' => $args['image'],
)
);
}
return $normalized;
}
/**
* Sanitize a track item.
*
* @param array $track Track data.
* @return array
*/
private static function prepare_track( $track ) {
return array(
'src' => esc_url_raw( $track['src'] ?? '' ),
'title' => sanitize_text_field( $track['title'] ?? __( 'Untitled Track', 'modern-audio-player' ) ),
'image' => esc_url_raw( $track['image'] ?? '' ),
);
}
/**
* Prepare playlist payload for the frontend runtime.
*
* @param array> $playlist Playlist items.
* @return array>
*/
private static function prepare_playlist_payload( $playlist ) {
$payload = array();
foreach ( $playlist as $track ) {
$hash = Analytics::build_source_hash( $track['src'] );
$payload[] = array(
'src' => $track['src'],
'title' => $track['title'],
'image' => $track['image'],
'hash' => $hash,
'nonce' => wp_create_nonce( 'map_track_play_' . $hash ),
);
}
return $payload;
}
/**
* Enqueue frontend block assets for shortcode and dynamic rendering.
*
* @return void
*/
private static function enqueue_block_assets() {
if ( is_admin() ) {
return;
}
$registry = \WP_Block_Type_Registry::get_instance();
$block = $registry->get_registered( 'velora/audio-player' );
if ( ! $block ) {
$block = $registry->get_registered( 'map/audio-player' );
}
if ( ! $block ) {
return;
}
foreach ( $block->style_handles as $handle ) {
wp_enqueue_style( $handle );
}
foreach ( $block->script_handles as $handle ) {
wp_enqueue_script( $handle );
}
}
}