$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 ); } } }