<code>
/*
Plugin Name: Playlist URL
Description: Adds a custom shortcode with skip/back buttons on the left and playlist support. there is still not skip or prev controls that work on the keyboard yet.
Author: Dennis Thomas (modified by ChatGPT)
Version: 0.5
*/
if ( ! defined( 'WPINC' ) ) {
die;
}
function dcwd_playlist_url_shortcode( $attr ) {
global $content_width;
$post = get_post();
static $instance = 0;
++$instance;
$atts = shortcode_atts(
array(
'type' => 'audio',
'order' => 'ASC',
'orderby' => 'menu_order ID',
'id' => $post ? $post->ID : 0,
'include' => '',
'exclude' => '',
'ids' => '',
'url' => '',
'style' => 'light',
'tracklist' => true,
'tracknumbers' => true,
'images' => true,
'artists' => true,
),
$attr,
'playlist_url'
);
$id = (int) $atts['id'];
if ( 'audio' !== $atts['type'] ) {
$atts['type'] = 'video';
}
$attachments = array();
$urls = array();
if ( ! empty( $atts['ids'] ) ) {
$atts['include'] = $atts['ids'];
}
if ( ! empty( $atts['include'] ) ) {
$args = array(
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => $atts['type'],
'order' => $atts['order'],
'orderby' => $atts['orderby'],
'include' => $atts['include'],
);
$_attachments = get_posts( $args );
foreach ( $_attachments as $attachment ) {
$attachments[ $attachment->ID ] = $attachment;
}
} elseif ( ! empty( $atts['exclude'] ) ) {
$attachments = get_children(
array(
'post_parent' => $id,
'exclude' => $atts['exclude'],
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => $atts['type'],
'order' => $atts['order'],
'orderby' => $atts['orderby'],
)
);
} else {
$attachments = get_children(
array(
'post_parent' => $id,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => $atts['type'],
'order' => $atts['order'],
'orderby' => $atts['orderby'],
)
);
}
if ( ! empty( $atts['url'] ) ) {
$urls = array_map( 'trim', explode( ',', $atts['url'] ) );
}
if ( empty( $attachments ) && empty( $urls ) ) {
return '';
}
$theme_width = empty( $content_width ) ? 640 : $content_width - 22;
$theme_height = round( 360 * $theme_width / 640 );
$data = array(
'type' => $atts['type'],
'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 );
$track = array(
'src' => $url,
'type' => $ftype['type'],
'title' => $attachment->post_title,
'caption' => $attachment->post_excerpt,
'description' => $attachment->post_content,
'meta' => array('length_formatted' => '0:00'),
);
$tracks[] = $track;
}
foreach ( $urls as $url ) {
$ftype = wp_check_filetype( $url );
$raw_name = urldecode( basename( $url ) );
$title = ucwords( preg_replace( '/[-_]/', ' ', pathinfo( $raw_name, PATHINFO_FILENAME ) ) );
$tracks[] = array(
'src' => esc_url( $url ),
'type' => $ftype['type'],
'title' => $title,
'meta' => array('length_formatted' => '0:00'),
);
}
$data['tracks'] = $tracks;
ob_start();
dcwd_playlist_scripts( $atts['type'] );
?>
<div class="wp-playlist wp-<?php echo esc_attr( $atts['type'] ); ?>-playlist wp-playlist-<?php echo esc_attr( $atts['style'] ); ?>">
<div class="playlist-controls">
<div class="button-group">
<div class="wp-playlist-prev" title="Previous track"></div>
<div class="wp-playlist-next" title="Next track"></div>
</div>
<<?php echo esc_attr( $atts['type'] ); ?> controls="controls" preload="none" width="<?php echo (int) $theme_width; ?>"></<?php echo esc_attr( $atts['type'] ); ?>>
</div>
<script type="application/json" class="wp-playlist-script"><?php echo wp_json_encode( $data ); ?></script>
</div>
<style>
.playlist-controls {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0;
margin: 0;
}
.button-group {
display: flex;
gap: 0;
}
.wp-playlist-prev,
.wp-playlist-next {
cursor: pointer;
width: 40px;
height: 40px;
background-color: black;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
user-select: none;
border: none;
border-radius: 0;
padding: 0;
margin: 0;
}
.wp-playlist-prev::before {
content: '⏮';
}
.wp-playlist-next::before {
content: '⏭';
}
</style>
<?php
return ob_get_clean();
}
add_shortcode( 'playlist_url', 'dcwd_playlist_url_shortcode' );
function dcwd_playlist_scripts( $type ) {
wp_enqueue_style( 'wp-mediaelement' );
wp_enqueue_script( 'wp-playlist' );
add_action( 'wp_footer', 'dcwd_underscore_playlist_templates', 0 );
add_action( 'admin_footer', 'dcwd_underscore_playlist_templates', 0 );
}
function dcwd_underscore_playlist_templates() {
?>
<script type="text/html" id="tmpl-wp-playlist-current-item">
<# if ( data.thumb && data.thumb.src ) { #>
<img src="{{ data.thumb.src }}" alt="" />
<# } #>
<div class="wp-playlist-caption">
<span class="wp-playlist-item-meta wp-playlist-item-title">{{ data.title }}</span>
<# if ( data.meta.album ) { #>
<span class="wp-playlist-item-meta wp-playlist-item-album">{{ data.meta.album }}</span>
<# } #>
<# if ( data.meta.artist ) { #>
<span class="wp-playlist-item-meta wp-playlist-item-artist">{{ data.meta.artist }}</span>
<# } #>
</div>
</script>
<script type="text/html" id="tmpl-wp-playlist-item">
<div class="wp-playlist-item">
<a class="wp-playlist-caption" href="{{ data.src }}">
{{ data.index ? ( data.index + '. ' ) : '' }}
<span class="wp-playlist-item-title">{{{ data.title }}}</span>
</a>
<# if ( data.meta.length_formatted ) { #>
<div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div>
<# } #>
</div>
</script>
<?php
}
</code>