JFIF $ d d 7 * Z qn )ZP 0 !1 A "#a$2Qr D8 a Ri [f \m I ykI w0 c u F c Rı?lO7 к _f˓ [C $殷WF< _W ԣ s Kc ëI zyQy /_LK ℂ ;C",p F A : /] =H ~ , l s / 9 ć: [=/#f ; ) x { ٛE Q )~ = 𘙲r*2~ a _V = ' ku m F D } KYYC )( { * g& f `툪 r y ` = ^ c J. I ]( * `wq 1dđ #̩ ͑ 0 ; H ] u 搂 @:~ ו K L N s h } O I R* 8 :2 ! lD JV o ( 3 = M ( z Ȱ+i *N A r6K n S l ) ! J J Ӂ* % ݉?|D } d5: eP0 R; {$X' x F @ .ÊB {,WJ uQ ɲRI ;9 Q E琯 6 2fT .DU J ;*c P A\IL Nj! J ۱+ O\ ͔ ] ޒ S J ȧ c%A N ol ՎprU L Zԛe r E2= X D Xg VQeӓ k y P7 U* o m Q I s ,K `) 6\G3t?pgjr m ۛجw luGt f h 9uyP 0D;Uڽ"OXl i f $ ) &|ML 0 Z rm1 [ H XP l P R0 'G=i 2N +0 e 2 ]] 9 VT P O 7 h(F * 癈 ' = Q VZD F,d ߬ ~ T X G[ `le 69C R(!S2! P < 0 x < !1AQ "Raq 02Br #S CTb ? Ζ "]mH5WR 7 k.ۛ ! } Q~ + yԏ z |@ T20 S~ K e k *zF f^2X *( @ 8 r ? C I uI|֓>^Ex LgNU Y+{.R Ѫ τV YT D I 6 2 '8Y2 7'\T P.6 d &˦ @V q i |8 -O Ε ] ʔ U= T L 8= ;6c | !qfF3 a ů& ~$ l} 'NW U s$U k^S V : U#6w + +s &r+n ڐ { @ 29gL u " T Ù M=6 (^ "7 r}=6Yݾ lC u hquymp Ǧ G jhs ǜNl ɻ} o 7 #S6 a w 4 ! OS r D 5 7 % | ?x> L | / nD 6?/8 w # [ ) L 7 +6〼T A T g ! %5 Mm Z /c -{1_Je "| ^$ 'O & ޱմ < H vNqϡQ$ < 6 &Nݛ ֨tǻN (9 n ru~'όN sMG& ,uH Kw *Ī 5@ u6 BԒ i YN * f 0 _ GhKW u lT <nj- 0{ M L~ | /a v\ E = ?1 \ɟuq Ђ Y O a K ޡP q $ H8o Q˼EV ~ k 0 h' L Pp M -M" m^ !6l nմ 쮝C U T . ! :vMǨb h ډ Vc r @~p # !1QAa q ? AL0L x LU 4 d ; x " 16ad0 7.\ߋ 0 = P l v/ " c A Ay A|2pO8"d MV #1] = \ " ˡ ] k v I9 d ۓ & q ) ǐq E ꞅ v "P )q X &Q З non]я u ג./ ; gZ$ M QР 2M ]h ni 0 P $ێ 3CɏGjώy : m c # S_ l@ '_ \m|C 2`K _ 1 cIvGc $? PCQ S+D ? K aײ k Fr b 8 ύ NEدg ĝ U 3 6 #Ik+ v { v i ( ވ ? a e) X xy/*@Q Ǽ Ծ ) $ IWO l c G; !i k{ gOŀ L ('x T rb $ w) R $ & N1E td U3Uȉ1p M"N* (DNy d96.(jQ) X 5 c QɎMy W ? Q *!R > 6=7)Xj 5`J ] e 8%t ! + ' ! 1Q 5 !1 AQaq ё #2 "0BRb ? G t^ ## .l lQ T $ v , ,m 㵜5u bV =sY+ @d { N ! dn O < .- B; _w J t 6;Q Jd. Q c%p{ 1 ,sND dF H I0 Гo X ш e黅X ۢ F :)[ F G X ƹ / w _c MeD ,ʡc c . W D tA $ j@ : ) - #u c1< @ۗ9F ) KJ- hpP] _ x [ q B lb p ʖ w q " LFGd ƶ * s +ډ_Zc " ? % t[I P 6 J ]# =ɺ V v v CGs Gh1 > )6 |e y ? L ӣ m,4 G W Ui ` ] uJVo VDG< SB 6ϏQ @ TiU l y O U 0 kfV ~ ~}S Z @ *WU Ui ## ; s /[ = ! 7}" W N ] ' (L ! ~ y5 g9T̅J kbM' +s :S +B) v @ M j e C f j E 0 Y\Qn z G 1 д~ W o{T 9?` Rm yhsy3 !HAD ] m c1 ~ 2 L S u7x T ;j$ `} 4-> L# vzŏ I L S ֭ T { r jGK C ; b pU= - ` Bs K. S F w4 M q] Z dH S 0 ) tLg
<?php
/**
* Server-side rendering of the `core/playlist` block.
*
* @package WordPress
*/
/**
* Renders the `core/playlist` block on server.
*
* @since 7.1.0
*
* @param array $attributes The block attributes.
* @param string $content The block content.
* @param WP_Block $block The block instance.
*
* @return string Returns the Playlist.
*/
function render_block_core_playlist( $attributes, $content, $block ) {
$playlist_id = wp_unique_id( 'playlist-' );
$playlist_tracks = array();
$tracks_data = array();
$show_play_button_artwork = ! empty( $attributes['showPlayButtonArtwork'] );
// Parse inner blocks to extract track data.
// This approach avoids duplicating track data in the HTML output.
if ( ! empty( $block->inner_blocks ) ) {
foreach ( $block->inner_blocks as $inner_block ) {
if ( 'core/playlist-track' === $inner_block->name ) {
$track_attributes = $inner_block->attributes;
if ( empty( $track_attributes['id'] ) ) {
continue;
}
$track_id = 'track-' . count( $playlist_tracks );
$playlist_tracks[] = $track_id;
// Extract track metadata from block attributes.
$title = isset( $track_attributes['title'] ) && ! empty( $track_attributes['title'] ) ? $track_attributes['title'] : __( 'Unknown title' );
$artist = $track_attributes['artist'] ?? '';
$album = $track_attributes['album'] ?? '';
$image = $track_attributes['image'] ?? '';
$image_alt = $track_attributes['imageAlt'] ?? '';
$url = $track_attributes['src'] ?? '';
$aria_label = $title;
if ( $title && $artist && $album ) {
$aria_label = sprintf(
/* translators: %1$s: track title, %2$s: artist name, %3$s: album name. */
_x( '%1$s by %2$s from the album %3$s', 'track title, artist name, album name' ),
$title,
$artist,
$album
);
}
// Data is passed to wp_interactivity_state() which JSON-encodes it,
// so we use wp_strip_all_tags() instead of esc_html() to prevent
// HTML injection without double-encoding. URLs still use esc_url().
$tracks_data[ $track_id ] = array(
'url' => esc_url( $url ),
'title' => wp_strip_all_tags( $title ),
'artist' => wp_strip_all_tags( $artist ),
'album' => wp_strip_all_tags( $album ),
'image' => esc_url( $image ),
'imageAlt' => wp_strip_all_tags( $image_alt ),
'ariaLabel' => wp_strip_all_tags( $aria_label ),
);
}
}
}
if ( empty( $playlist_tracks ) ) {
return '';
}
wp_enqueue_script_module( '@wordpress/block-library/playlist/view' );
// Add the playlist tracks to the global state,
// but keep them isolated from other playlists with the help of playlistId.
wp_interactivity_state(
'core/playlist',
array(
'playlists' => array(
$playlist_id => array(
'tracks' => $tracks_data,
),
),
)
);
// Add waveform player container with translated button labels.
$label_play = esc_attr__( 'Play' );
$label_pause = esc_attr__( 'Pause' );
$label_seek = esc_attr__( 'Seek' );
/* translators: %1$s: current audio time, %2$s: total audio duration. */
$label_seek_value = esc_attr_x(
'%1$s of %2$s',
'audio current time of total duration'
);
$waveform_color_attribute = '';
$waveform_gradient_attribute = '';
$waveform_background_color_attribute = '';
$waveform_background_gradient_attribute = '';
if ( ! empty( $attributes['waveformColor'] ) ) {
$waveform_color_attribute = sprintf(
' data-waveform-player-color="%s"',
esc_attr( $attributes['waveformColor'] )
);
}
if ( ! empty( $attributes['waveformGradient'] ) ) {
$waveform_gradient_attribute = sprintf(
' data-waveform-player-gradient="%s"',
esc_attr( $attributes['waveformGradient'] )
);
}
if ( ! empty( $attributes['waveformBackgroundColor'] ) ) {
$waveform_background_color_attribute = sprintf(
' data-waveform-player-background-color="%s"',
esc_attr( $attributes['waveformBackgroundColor'] )
);
}
if ( ! empty( $attributes['waveformBackgroundGradient'] ) ) {
$waveform_background_gradient_attribute = sprintf(
' data-waveform-player-background-gradient="%s"',
esc_attr( $attributes['waveformBackgroundGradient'] )
);
}
$html = '<div class="wp-block-playlist__waveform-player"' .
$waveform_color_attribute .
$waveform_gradient_attribute .
$waveform_background_color_attribute .
$waveform_background_gradient_attribute . '
data-wp-watch="callbacks.initWaveformPlayer"
data-label-play="' . $label_play . '"
data-label-pause="' . $label_pause . '"
data-label-seek="' . $label_seek . '"
data-label-seek-value="' . $label_seek_value . '"
></div>';
// Add the waveform player container inside the figure.
$figure = null;
preg_match( '/<figure[^>]*>/', $content, $figure );
if ( ! empty( $figure[0] ) ) {
$content = preg_replace( '/(<figure[^>]*>)/', '$1' . $html, $content, 1 );
}
$processor = new WP_HTML_Tag_Processor( $content );
$processor->next_tag( 'figure' );
$processor->set_attribute( 'data-wp-interactive', 'core/playlist' );
$waveform_style = $attributes['waveformStyle'] ?? 'bars';
$processor->set_attribute(
'data-wp-context',
wp_json_encode(
array(
'playlistId' => $playlist_id,
'currentId' => $playlist_tracks[0],
'isPlaying' => false,
'tracks' => $playlist_tracks,
'waveformStyle' => $waveform_style,
'showPlayButtonArtwork' => $show_play_button_artwork,
'labelPauseTrack' => __( 'Pause' ),
'labelSelectTrack' => __( 'Play' ),
)
)
);
// Track IDs are render-time only. Add them after inner blocks have rendered
// so track buttons can update the Interactivity API state without storing
// persistent unique IDs in post content.
$track_index = 0;
while ( $processor->next_tag( array( 'class_name' => 'wp-block-playlist-track__button' ) ) ) {
$track_id = $playlist_tracks[ $track_index ] ?? null;
if ( null === $track_id ) {
break;
}
$processor->set_attribute(
'data-wp-context',
wp_json_encode(
array(
'trackId' => $track_id,
)
)
);
++$track_index;
}
return $processor->get_updated_html();
}
/**
* Registers the `core/playlist` block on server.
*
* @since 7.1.0
*/
function register_block_core_playlist() {
register_block_type_from_metadata(
__DIR__ . '/playlist',
array(
'render_callback' => 'render_block_core_playlist',
)
);
}
add_action( 'init', 'register_block_core_playlist' );