wp_playlist_shortcode

The timeline below displays how wordpress function wp_playlist_shortcode has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.5

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    ++$instance;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // Terminate the shortcode execution if the user cannot read the post or it is password-protected.
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID, '.svg');
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 6.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    ++$instance;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 3.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 6.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 2.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 6.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 1.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 6.1

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 9.8

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.9

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 8.8

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 7.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .10

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 6.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .12

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.6

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = (int) $atts['id'];
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
		<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"
		<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
		<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .13

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.5

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Returning a non-empty value from the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .14

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('audio' !== $atts['type']) {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // Default padding and border of wrapper.
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // Don't pass strings to JSON, will be truthy in JS.
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 3.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .16

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if (!empty($output)) {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 2.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .20

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 2.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .19

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 1.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .17

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.1

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="
				<?php 
    echo (int) $theme_width;
    ?>
	"
	<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>
	></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol>
	<?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?>
	</ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 0.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .20

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 9.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .24

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 6.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .27

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.6

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filters the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Prints and enqueues playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 5.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .30

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .31

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 3.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .32

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.3

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @global int $content_width
 * @staticvar int $instance
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 2.4

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .36

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.2

/**
 * Builds the Playlist shortcode output.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     * @since 4.2.0 The `$instance` parameter was added.
     *
     * @param string $output   Playlist output. Default empty.
     * @param array  $attr     An array of shortcode attributes.
     * @param int    $instance Unique numeric ID of this playlist shortcode instance.
     */
    $output = apply_filters('post_playlist', '', $attr, $instance);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 1.5

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .40

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 1.4

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .39

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (!empty($args['post_parent'])) {
        $post_parent = get_post($id);
        // terminate the shortcode execution if user cannot read the post or password-protected
        if (!current_user_can('read_post', $post_parent->ID) || post_password_required($post_parent)) {
            return '';
        }
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.1

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo wp_json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 0.1

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC', or 'RAND'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('RAND' == $atts['order']) {
        $atts['orderby'] = 'none';
    }
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 4.0

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr {
 *     Array of default playlist attributes.
 *
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
 *                                 Accepts 'ASC', 'DESC', or 'RAND'. Default 'ASC'.
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
 *                                 passed, this defaults to the order of the $ids array ('post__in').
 *                                 Otherwise default is 'menu_order ID'.
 *     @type int     $id           If an explicit $ids array is not present, this parameter
 *                                 will determine which attachments are used for the playlist.
 *                                 Default is the current post ID.
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
 *                                 a playlist will be created from all $type attachments of $id.
 *                                 Default empty.
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
 *                                 thumbnail). Default true.
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
 * }
 *
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    $atts = shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist');
    $id = intval($atts['id']);
    if ('RAND' == $atts['order']) {
        $atts['orderby'] = 'none';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $atts['type'], 'order' => $atts['order'], 'orderby' => $atts['orderby']);
    if (!empty($atts['include'])) {
        $args['include'] = $atts['include'];
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($atts['exclude'])) {
        $args['post_parent'] = $id;
        $args['exclude'] = $atts['exclude'];
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = array(
        'type' => $atts['type'],
        // don't pass strings to JSON, will be truthy in JS
        'tracklist' => wp_validate_boolean($atts['tracklist']),
        'tracknumbers' => wp_validate_boolean($atts['tracknumbers']),
        'images' => wp_validate_boolean($atts['images']),
        'artists' => wp_validate_boolean($atts['artists']),
    );
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $atts['type']) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($atts['images']) {
            $thumb_id = get_post_thumbnail_id($attachment->ID);
            if (!empty($thumb_id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($thumb_id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($atts['type']);
    $safe_style = esc_attr($atts['style']);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $atts['type'], $atts['style']);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $atts['type']) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json" class="wp-playlist-script"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 9.3

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr Playlist shortcode attributes.
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    extract(shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist'));
    $id = intval($id);
    if ('RAND' == $order) {
        $orderby = 'none';
    }
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby);
    if (!empty($include)) {
        $args['include'] = $include;
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($exclude)) {
        $args['post_parent'] = $id;
        $args['exclude'] = $exclude;
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = compact('type');
    // don't pass strings to JSON, will be truthy in JS
    foreach (array('tracklist', 'tracknumbers', 'images', 'artists') as $key) {
        $data[$key] = filter_var(${$key}, FILTER_VALIDATE_BOOLEAN);
    }
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $type) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($images) {
            $id = get_post_thumbnail_id($attachment->ID);
            if (!empty($id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($type);
    $safe_style = esc_attr($style);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $type, $style);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $type) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 9.2

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr Playlist shortcode attributes.
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    extract(shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist'));
    $id = intval($id);
    if ('RAND' == $order) {
        $orderby = 'none';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby);
    if (!empty($include)) {
        $args['include'] = $include;
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($exclude)) {
        $args['post_parent'] = $id;
        $args['exclude'] = $exclude;
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = compact('type');
    // don't pass strings to JSON, will be truthy in JS
    foreach (array('tracklist', 'tracknumbers', 'images', 'artists') as $key) {
        $data[$key] = filter_var(${$key}, FILTER_VALIDATE_BOOLEAN);
    }
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $type) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($images) {
            $id = get_post_thumbnail_id($attachment->ID);
            if (!empty($id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($type);
    $safe_style = esc_attr($style);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $type, $style);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $type) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: .10

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr Playlist shortcode attributes.
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    extract(shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist'));
    $id = intval($id);
    if ('RAND' == $order) {
        $orderby = 'none';
    }
    if ($atts['type'] !== 'audio') {
        $atts['type'] = 'video';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby);
    if (!empty($include)) {
        $args['include'] = $include;
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($exclude)) {
        $args['post_parent'] = $id;
        $args['exclude'] = $exclude;
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = compact('type');
    // don't pass strings to JSON, will be truthy in JS
    foreach (array('tracklist', 'tracknumbers', 'images', 'artists') as $key) {
        $data[$key] = filter_var(${$key}, FILTER_VALIDATE_BOOLEAN);
    }
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $type) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($images) {
            $id = get_post_thumbnail_id($attachment->ID);
            if (!empty($id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($type);
    $safe_style = esc_attr($style);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $type, $style);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $type) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 9.1

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr Playlist shortcode attributes.
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    extract(shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist'));
    $id = intval($id);
    if ('RAND' == $order) {
        $orderby = 'none';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby);
    if (!empty($include)) {
        $args['include'] = $include;
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($exclude)) {
        $args['post_parent'] = $id;
        $args['exclude'] = $exclude;
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = compact('type');
    // don't pass strings to JSON, will be truthy in JS
    foreach (array('tracklist', 'tracknumbers', 'images', 'artists') as $key) {
        $data[$key] = filter_var(${$key}, FILTER_VALIDATE_BOOLEAN);
    }
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $type) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($images) {
            $id = get_post_thumbnail_id($attachment->ID);
            if (!empty($id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($type);
    $safe_style = esc_attr($style);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $type, $style);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $type) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    } else {
        echo ' style="visibility: hidden"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}

WordPress Version: 3.9

/**
 * The playlist shortcode.
 *
 * This implements the functionality of the playlist shortcode for displaying
 * a collection of WordPress audio or video files in a post.
 *
 * @since 3.9.0
 *
 * @param array $attr Playlist shortcode attributes.
 * @return string Playlist output. Empty string if the passed type is unsupported.
 */
function wp_playlist_shortcode($attr)
{
    global $content_width;
    $post = get_post();
    static $instance = 0;
    $instance++;
    if (!empty($attr['ids'])) {
        // 'ids' is explicitly ordered, unless you specify otherwise.
        if (empty($attr['orderby'])) {
            $attr['orderby'] = 'post__in';
        }
        $attr['include'] = $attr['ids'];
    }
    /**
     * Filter the playlist output.
     *
     * Passing a non-empty value to the filter will short-circuit generation
     * of the default playlist output, returning the passed value instead.
     *
     * @since 3.9.0
     *
     * @param string $output Playlist output. Default empty.
     * @param array  $attr   An array of shortcode attributes.
     */
    $output = apply_filters('post_playlist', '', $attr);
    if ($output != '') {
        return $output;
    }
    /*
     * We're trusting author input, so let's at least make sure it looks
     * like a valid orderby statement.
     */
    if (isset($attr['orderby'])) {
        $attr['orderby'] = sanitize_sql_orderby($attr['orderby']);
        if (!$attr['orderby']) {
            unset($attr['orderby']);
        }
    }
    extract(shortcode_atts(array('type' => 'audio', 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => $post ? $post->ID : 0, 'include' => '', 'exclude' => '', 'style' => 'light', 'tracklist' => true, 'tracknumbers' => true, 'images' => true, 'artists' => true), $attr, 'playlist'));
    $id = intval($id);
    if ('RAND' == $order) {
        $orderby = 'none';
    }
    $args = array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby);
    if (!empty($include)) {
        $args['include'] = $include;
        $_attachments = get_posts($args);
        $attachments = array();
        foreach ($_attachments as $key => $val) {
            $attachments[$val->ID] = $_attachments[$key];
        }
    } elseif (!empty($exclude)) {
        $args['post_parent'] = $id;
        $args['exclude'] = $exclude;
        $attachments = get_children($args);
    } else {
        $args['post_parent'] = $id;
        $attachments = get_children($args);
    }
    if (empty($attachments)) {
        return '';
    }
    if (is_feed()) {
        $output = "\n";
        foreach ($attachments as $att_id => $attachment) {
            $output .= wp_get_attachment_link($att_id) . "\n";
        }
        return $output;
    }
    $outer = 22;
    // default padding and border of wrapper
    $default_width = 640;
    $default_height = 360;
    $theme_width = empty($content_width) ? $default_width : ($content_width - $outer);
    $theme_height = empty($content_width) ? $default_height : round($default_height * $theme_width / $default_width);
    $data = compact('type');
    // don't pass strings to JSON, will be truthy in JS
    foreach (array('tracklist', 'tracknumbers', 'images', 'artists') as $key) {
        $data[$key] = filter_var(${$key}, FILTER_VALIDATE_BOOLEAN);
    }
    $tracks = array();
    foreach ($attachments as $attachment) {
        $url = wp_get_attachment_url($attachment->ID);
        $ftype = wp_check_filetype($url, wp_get_mime_types());
        $track = array('src' => $url, 'type' => $ftype['type'], 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content);
        $track['meta'] = array();
        $meta = wp_get_attachment_metadata($attachment->ID);
        if (!empty($meta)) {
            foreach (wp_get_attachment_id3_keys($attachment) as $key => $label) {
                if (!empty($meta[$key])) {
                    $track['meta'][$key] = $meta[$key];
                }
            }
            if ('video' === $type) {
                if (!empty($meta['width']) && !empty($meta['height'])) {
                    $width = $meta['width'];
                    $height = $meta['height'];
                    $theme_height = round($height * $theme_width / $width);
                } else {
                    $width = $default_width;
                    $height = $default_height;
                }
                $track['dimensions'] = array('original' => compact('width', 'height'), 'resized' => array('width' => $theme_width, 'height' => $theme_height));
            }
        }
        if ($images) {
            $id = get_post_thumbnail_id($attachment->ID);
            if (!empty($id)) {
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'full');
                $track['image'] = compact('src', 'width', 'height');
                list($src, $width, $height) = wp_get_attachment_image_src($id, 'thumbnail');
                $track['thumb'] = compact('src', 'width', 'height');
            } else {
                $src = wp_mime_type_icon($attachment->ID);
                $width = 48;
                $height = 64;
                $track['image'] = compact('src', 'width', 'height');
                $track['thumb'] = compact('src', 'width', 'height');
            }
        }
        $tracks[] = $track;
    }
    $data['tracks'] = $tracks;
    $safe_type = esc_attr($type);
    $safe_style = esc_attr($style);
    ob_start();
    if (1 === $instance) {
        /**
         * Print and enqueue playlist scripts, styles, and JavaScript templates.
         *
         * @since 3.9.0
         *
         * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
         * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
         */
        do_action('wp_playlist_scripts', $type, $style);
    }
    ?>
<div class="wp-playlist wp-<?php 
    echo $safe_type;
    ?>-playlist wp-playlist-<?php 
    echo $safe_style;
    ?>">
	<?php 
    if ('audio' === $type) {
        ?>
	<div class="wp-playlist-current-item"></div>
	<?php 
    }
    ?>
	<<?php 
    echo $safe_type;
    ?> controls="controls" preload="none" width="<?php 
    echo (int) $theme_width;
    ?>"<?php 
    if ('video' === $safe_type) {
        echo ' height="', (int) $theme_height, '"';
    }
    ?>></<?php 
    echo $safe_type;
    ?>>
	<div class="wp-playlist-next"></div>
	<div class="wp-playlist-prev"></div>
	<noscript>
	<ol><?php 
    foreach ($attachments as $att_id => $attachment) {
        printf('<li>%s</li>', wp_get_attachment_link($att_id));
    }
    ?></ol>
	</noscript>
	<script type="application/json"><?php 
    echo json_encode($data);
    ?></script>
</div>
	<?php 
    return ob_get_clean();
}