__( 'Playlists', 'trueplayer' ), 'public' => false, 'show_ui' => false, 'show_in_menu' => false, 'show_in_rest' => false, 'supports' => [ 'title' ], 'exclude_from_search' => true, ] ); register_post_meta( self::POST_TYPE, '_trueplayer_playlist', [ 'type' => 'string', 'single' => true, 'show_in_rest' => false, 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ] ); } public static function get_config( $playlist_id ) { $raw = get_post_meta( (int) $playlist_id, '_trueplayer_playlist', true ); $cfg = is_string( $raw ) && '' !== $raw ? json_decode( $raw, true ) : ( is_array( $raw ) ? $raw : [] ); return is_array( $cfg ) ? $cfg : []; } /** * Build the client payload for one video (id, title, poster, stripped config). */ public static function video_payload( $video_id ) { $video_id = (int) $video_id; if ( get_post_type( $video_id ) !== TRUEPLAYER_VIDEO_POST_TYPE ) { return null; } // Playlist members do not route through Shortcode::resolved_config(), so // the media type is resolved here for the same reason it is there: the // preset merge and the per-item facade both branch on it. $config = Media::normalize_config( Helper::get_video_config( $video_id ) ); $config = Helper::with_derived_poster( Helper::apply_preset( $config ) ); /** Same add-on hook the shortcode runs (see Shortcode::resolved_config). */ $config = (array) apply_filters( 'trueplayer/player/config', $config, $video_id ); $config = Shortcode::strip_answer_keys( $config ); return [ 'videoId' => $video_id, 'title' => get_the_title( $video_id ), 'poster' => $config['source']['poster'] ?? '', 'config' => $config, ]; } public function render( $atts ) { $atts = shortcode_atts( [ 'id' => 0 ], $atts, self::TAG ); $playlist_id = (int) $atts['id']; if ( ! $playlist_id || get_post_type( $playlist_id ) !== self::POST_TYPE ) { return ''; } $cfg = self::get_config( $playlist_id ); $items = []; foreach ( (array) ( $cfg['videos'] ?? [] ) as $vid ) { $payload = self::video_payload( $vid ); if ( $payload ) { $items[] = $payload; } } if ( empty( $items ) ) { return ''; } wp_enqueue_style( Assets::FRONTEND_STYLE_HANDLE ); wp_enqueue_script( Assets::FRONTEND_SCRIPT_HANDLE ); $data = [ 'title' => $cfg['title'] ?? get_the_title( $playlist_id ), 'layout' => in_array( $cfg['layout'] ?? 'sidebar', [ 'sidebar', 'grid' ], true ) ? $cfg['layout'] : 'sidebar', 'autoplayNext' => ! empty( $cfg['autoplayNext'] ), 'showTitles' => ! isset( $cfg['showTitles'] ) || ! empty( $cfg['showTitles'] ), 'items' => $items, ]; // Config travels as an escaped data attribute, read by mount.js. return sprintf( '
', esc_attr( wp_json_encode( $data ) ) ); } }