You've already forked wp-fedistream
feat: Initial release v0.1.0
WP FediStream - Stream music over ActivityPub Features: - Custom post types: Artist, Album, Track, Playlist - Custom taxonomies: Genre, Mood, License - User roles: Artist, Label - Admin dashboard with statistics - Frontend templates and shortcodes - Audio player with queue management - ActivityPub integration with actor support - WooCommerce product types for albums/tracks - User library with favorites and history - Notification system (in-app and email) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
528
includes/Frontend/TemplateLoader.php
Normal file
528
includes/Frontend/TemplateLoader.php
Normal file
@@ -0,0 +1,528 @@
|
||||
<?php
|
||||
/**
|
||||
* Template loader for frontend display.
|
||||
*
|
||||
* @package WP_FediStream
|
||||
*/
|
||||
|
||||
namespace WP_FediStream\Frontend;
|
||||
|
||||
use WP_FediStream\Plugin;
|
||||
|
||||
// Prevent direct file access.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* TemplateLoader class.
|
||||
*
|
||||
* Handles loading custom templates for FediStream post types.
|
||||
*/
|
||||
class TemplateLoader {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'template_include', array( $this, 'template_include' ) );
|
||||
add_filter( 'body_class', array( $this, 'body_class' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Include custom templates for FediStream post types.
|
||||
*
|
||||
* @param string $template Template path.
|
||||
* @return string Modified template path.
|
||||
*/
|
||||
public function template_include( string $template ): string {
|
||||
// Check if we're on a FediStream page.
|
||||
if ( is_singular( 'fedistream_artist' ) ) {
|
||||
return $this->get_template( 'single-artist' );
|
||||
}
|
||||
|
||||
if ( is_singular( 'fedistream_album' ) ) {
|
||||
return $this->get_template( 'single-album' );
|
||||
}
|
||||
|
||||
if ( is_singular( 'fedistream_track' ) ) {
|
||||
return $this->get_template( 'single-track' );
|
||||
}
|
||||
|
||||
if ( is_singular( 'fedistream_playlist' ) ) {
|
||||
return $this->get_template( 'single-playlist' );
|
||||
}
|
||||
|
||||
if ( is_post_type_archive( 'fedistream_artist' ) ) {
|
||||
return $this->get_template( 'archive-artist' );
|
||||
}
|
||||
|
||||
if ( is_post_type_archive( 'fedistream_album' ) ) {
|
||||
return $this->get_template( 'archive-album' );
|
||||
}
|
||||
|
||||
if ( is_post_type_archive( 'fedistream_track' ) ) {
|
||||
return $this->get_template( 'archive-track' );
|
||||
}
|
||||
|
||||
if ( is_post_type_archive( 'fedistream_playlist' ) ) {
|
||||
return $this->get_template( 'archive-playlist' );
|
||||
}
|
||||
|
||||
if ( is_tax( 'fedistream_genre' ) ) {
|
||||
return $this->get_template( 'taxonomy-genre' );
|
||||
}
|
||||
|
||||
if ( is_tax( 'fedistream_mood' ) ) {
|
||||
return $this->get_template( 'taxonomy-mood' );
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template file path.
|
||||
*
|
||||
* First checks theme for override, then uses plugin template.
|
||||
*
|
||||
* @param string $template_name Template name without extension.
|
||||
* @return string Template path.
|
||||
*/
|
||||
private function get_template( string $template_name ): string {
|
||||
// Check theme for override.
|
||||
$theme_template = locate_template(
|
||||
array(
|
||||
"fedistream/{$template_name}.php",
|
||||
"fedistream/{$template_name}.twig",
|
||||
)
|
||||
);
|
||||
|
||||
if ( $theme_template ) {
|
||||
return $theme_template;
|
||||
}
|
||||
|
||||
// Use plugin template wrapper.
|
||||
return WP_FEDISTREAM_PATH . 'includes/Frontend/template-wrapper.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add FediStream classes to body.
|
||||
*
|
||||
* @param array $classes Body classes.
|
||||
* @return array Modified classes.
|
||||
*/
|
||||
public function body_class( array $classes ): array {
|
||||
if ( $this->is_fedistream_page() ) {
|
||||
$classes[] = 'fedistream';
|
||||
|
||||
if ( is_singular( 'fedistream_artist' ) ) {
|
||||
$classes[] = 'fedistream-artist';
|
||||
$classes[] = 'fedistream-single';
|
||||
} elseif ( is_singular( 'fedistream_album' ) ) {
|
||||
$classes[] = 'fedistream-album';
|
||||
$classes[] = 'fedistream-single';
|
||||
} elseif ( is_singular( 'fedistream_track' ) ) {
|
||||
$classes[] = 'fedistream-track';
|
||||
$classes[] = 'fedistream-single';
|
||||
} elseif ( is_singular( 'fedistream_playlist' ) ) {
|
||||
$classes[] = 'fedistream-playlist';
|
||||
$classes[] = 'fedistream-single';
|
||||
} elseif ( is_post_type_archive( 'fedistream_artist' ) ) {
|
||||
$classes[] = 'fedistream-archive';
|
||||
$classes[] = 'fedistream-artists';
|
||||
} elseif ( is_post_type_archive( 'fedistream_album' ) ) {
|
||||
$classes[] = 'fedistream-archive';
|
||||
$classes[] = 'fedistream-albums';
|
||||
} elseif ( is_post_type_archive( 'fedistream_track' ) ) {
|
||||
$classes[] = 'fedistream-archive';
|
||||
$classes[] = 'fedistream-tracks';
|
||||
} elseif ( is_post_type_archive( 'fedistream_playlist' ) ) {
|
||||
$classes[] = 'fedistream-archive';
|
||||
$classes[] = 'fedistream-playlists';
|
||||
} elseif ( is_tax( 'fedistream_genre' ) || is_tax( 'fedistream_mood' ) ) {
|
||||
$classes[] = 'fedistream-archive';
|
||||
$classes[] = 'fedistream-taxonomy';
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current page is a FediStream page.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_fedistream_page(): bool {
|
||||
return is_singular( array( 'fedistream_artist', 'fedistream_album', 'fedistream_track', 'fedistream_playlist' ) )
|
||||
|| is_post_type_archive( array( 'fedistream_artist', 'fedistream_album', 'fedistream_track', 'fedistream_playlist' ) )
|
||||
|| is_tax( array( 'fedistream_genre', 'fedistream_mood', 'fedistream_license' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template context for current page.
|
||||
*
|
||||
* @return array Template context.
|
||||
*/
|
||||
public static function get_context(): array {
|
||||
$context = array(
|
||||
'site_name' => get_bloginfo( 'name' ),
|
||||
'site_url' => home_url(),
|
||||
'is_singular' => is_singular(),
|
||||
'is_archive' => is_archive(),
|
||||
'current_url' => get_permalink(),
|
||||
);
|
||||
|
||||
if ( is_singular() ) {
|
||||
global $post;
|
||||
$context['post'] = self::get_post_data( $post );
|
||||
}
|
||||
|
||||
if ( is_post_type_archive() || is_tax() ) {
|
||||
$context['posts'] = self::get_archive_posts();
|
||||
$context['pagination'] = self::get_pagination();
|
||||
$context['archive_title'] = self::get_archive_title();
|
||||
$context['archive_description'] = self::get_archive_description();
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post data for template.
|
||||
*
|
||||
* @param \WP_Post $post Post object.
|
||||
* @return array Post data.
|
||||
*/
|
||||
public static function get_post_data( \WP_Post $post ): array {
|
||||
$data = array(
|
||||
'id' => $post->ID,
|
||||
'title' => get_the_title( $post ),
|
||||
'content' => apply_filters( 'the_content', $post->post_content ),
|
||||
'excerpt' => get_the_excerpt( $post ),
|
||||
'permalink' => get_permalink( $post ),
|
||||
'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'large' ),
|
||||
'date' => get_the_date( '', $post ),
|
||||
'author' => get_the_author_meta( 'display_name', $post->post_author ),
|
||||
);
|
||||
|
||||
// Add post type specific data.
|
||||
switch ( $post->post_type ) {
|
||||
case 'fedistream_artist':
|
||||
$data = array_merge( $data, self::get_artist_data( $post->ID ) );
|
||||
break;
|
||||
case 'fedistream_album':
|
||||
$data = array_merge( $data, self::get_album_data( $post->ID ) );
|
||||
break;
|
||||
case 'fedistream_track':
|
||||
$data = array_merge( $data, self::get_track_data( $post->ID ) );
|
||||
break;
|
||||
case 'fedistream_playlist':
|
||||
$data = array_merge( $data, self::get_playlist_data( $post->ID ) );
|
||||
break;
|
||||
}
|
||||
|
||||
// Add taxonomies.
|
||||
$data['genres'] = self::get_terms( $post->ID, 'fedistream_genre' );
|
||||
$data['moods'] = self::get_terms( $post->ID, 'fedistream_mood' );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get artist-specific data.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return array Artist data.
|
||||
*/
|
||||
private static function get_artist_data( int $post_id ): array {
|
||||
$type = get_post_meta( $post_id, '_fedistream_artist_type', true ) ?: 'solo';
|
||||
$types = array(
|
||||
'solo' => __( 'Solo Artist', 'wp-fedistream' ),
|
||||
'band' => __( 'Band', 'wp-fedistream' ),
|
||||
'duo' => __( 'Duo', 'wp-fedistream' ),
|
||||
'collective' => __( 'Collective', 'wp-fedistream' ),
|
||||
);
|
||||
|
||||
$albums = get_posts(
|
||||
array(
|
||||
'post_type' => 'fedistream_album',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'publish',
|
||||
'meta_key' => '_fedistream_album_artist',
|
||||
'meta_value' => $post_id,
|
||||
'orderby' => 'meta_value',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_fedistream_album_release_date',
|
||||
'compare' => 'EXISTS',
|
||||
),
|
||||
),
|
||||
'order' => 'DESC',
|
||||
)
|
||||
);
|
||||
|
||||
return array(
|
||||
'artist_type' => $type,
|
||||
'artist_type_label' => $types[ $type ] ?? $types['solo'],
|
||||
'formed_date' => get_post_meta( $post_id, '_fedistream_artist_formed_date', true ),
|
||||
'location' => get_post_meta( $post_id, '_fedistream_artist_location', true ),
|
||||
'website' => get_post_meta( $post_id, '_fedistream_artist_website', true ),
|
||||
'social_links' => get_post_meta( $post_id, '_fedistream_artist_social_links', true ) ?: array(),
|
||||
'members' => get_post_meta( $post_id, '_fedistream_artist_members', true ) ?: array(),
|
||||
'albums' => array_map( array( __CLASS__, 'get_post_data' ), $albums ),
|
||||
'album_count' => count( $albums ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get album-specific data.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return array Album data.
|
||||
*/
|
||||
private static function get_album_data( int $post_id ): array {
|
||||
$type = get_post_meta( $post_id, '_fedistream_album_type', true ) ?: 'album';
|
||||
$types = array(
|
||||
'album' => __( 'Album', 'wp-fedistream' ),
|
||||
'ep' => __( 'EP', 'wp-fedistream' ),
|
||||
'single' => __( 'Single', 'wp-fedistream' ),
|
||||
'compilation' => __( 'Compilation', 'wp-fedistream' ),
|
||||
'live' => __( 'Live Album', 'wp-fedistream' ),
|
||||
'remix' => __( 'Remix Album', 'wp-fedistream' ),
|
||||
);
|
||||
$artist_id = get_post_meta( $post_id, '_fedistream_album_artist', true );
|
||||
|
||||
$tracks = get_posts(
|
||||
array(
|
||||
'post_type' => 'fedistream_track',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'publish',
|
||||
'meta_key' => '_fedistream_track_album',
|
||||
'meta_value' => $post_id,
|
||||
'orderby' => 'meta_value_num',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => '_fedistream_track_number',
|
||||
'compare' => 'EXISTS',
|
||||
),
|
||||
),
|
||||
'order' => 'ASC',
|
||||
)
|
||||
);
|
||||
|
||||
return array(
|
||||
'album_type' => $type,
|
||||
'album_type_label' => $types[ $type ] ?? $types['album'],
|
||||
'release_date' => get_post_meta( $post_id, '_fedistream_album_release_date', true ),
|
||||
'release_year' => date( 'Y', strtotime( get_post_meta( $post_id, '_fedistream_album_release_date', true ) ?: 'now' ) ),
|
||||
'artist_id' => $artist_id,
|
||||
'artist_name' => $artist_id ? get_the_title( $artist_id ) : '',
|
||||
'artist_url' => $artist_id ? get_permalink( $artist_id ) : '',
|
||||
'upc' => get_post_meta( $post_id, '_fedistream_album_upc', true ),
|
||||
'catalog_number' => get_post_meta( $post_id, '_fedistream_album_catalog_number', true ),
|
||||
'total_tracks' => count( $tracks ),
|
||||
'total_duration' => (int) get_post_meta( $post_id, '_fedistream_album_total_duration', true ),
|
||||
'tracks' => array_map( array( __CLASS__, 'get_post_data' ), $tracks ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get track-specific data.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return array Track data.
|
||||
*/
|
||||
private static function get_track_data( int $post_id ): array {
|
||||
$album_id = get_post_meta( $post_id, '_fedistream_track_album', true );
|
||||
$audio_file = get_post_meta( $post_id, '_fedistream_track_audio_file', true );
|
||||
$artists = get_post_meta( $post_id, '_fedistream_track_artists', true ) ?: array();
|
||||
$duration = (int) get_post_meta( $post_id, '_fedistream_track_duration', true );
|
||||
|
||||
$artist_data = array();
|
||||
foreach ( $artists as $artist_id ) {
|
||||
$artist = get_post( $artist_id );
|
||||
if ( $artist ) {
|
||||
$artist_data[] = array(
|
||||
'id' => $artist_id,
|
||||
'name' => $artist->post_title,
|
||||
'url' => get_permalink( $artist_id ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'track_number' => (int) get_post_meta( $post_id, '_fedistream_track_number', true ),
|
||||
'disc_number' => (int) get_post_meta( $post_id, '_fedistream_track_disc_number', true ) ?: 1,
|
||||
'duration' => $duration,
|
||||
'duration_formatted' => $duration ? sprintf( '%d:%02d', floor( $duration / 60 ), $duration % 60 ) : '',
|
||||
'audio_url' => $audio_file ? wp_get_attachment_url( $audio_file ) : '',
|
||||
'audio_format' => get_post_meta( $post_id, '_fedistream_track_audio_format', true ),
|
||||
'bpm' => (int) get_post_meta( $post_id, '_fedistream_track_bpm', true ),
|
||||
'key' => get_post_meta( $post_id, '_fedistream_track_key', true ),
|
||||
'explicit' => (bool) get_post_meta( $post_id, '_fedistream_track_explicit', true ),
|
||||
'isrc' => get_post_meta( $post_id, '_fedistream_track_isrc', true ),
|
||||
'album_id' => $album_id,
|
||||
'album_title' => $album_id ? get_the_title( $album_id ) : '',
|
||||
'album_url' => $album_id ? get_permalink( $album_id ) : '',
|
||||
'album_artwork' => $album_id ? get_the_post_thumbnail_url( $album_id, 'medium' ) : '',
|
||||
'artists' => $artist_data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get playlist-specific data.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return array Playlist data.
|
||||
*/
|
||||
private static function get_playlist_data( int $post_id ): array {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'fedistream_playlist_tracks';
|
||||
$duration = (int) get_post_meta( $post_id, '_fedistream_playlist_total_duration', true );
|
||||
|
||||
// Get tracks.
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
$track_ids = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT track_id FROM $table WHERE playlist_id = %d ORDER BY position ASC",
|
||||
$post_id
|
||||
)
|
||||
);
|
||||
|
||||
$tracks = array();
|
||||
foreach ( $track_ids as $track_id ) {
|
||||
$track = get_post( $track_id );
|
||||
if ( $track && 'publish' === $track->post_status ) {
|
||||
$tracks[] = self::get_post_data( $track );
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'visibility' => get_post_meta( $post_id, '_fedistream_playlist_visibility', true ) ?: 'public',
|
||||
'collaborative' => (bool) get_post_meta( $post_id, '_fedistream_playlist_collaborative', true ),
|
||||
'federated' => (bool) get_post_meta( $post_id, '_fedistream_playlist_federated', true ),
|
||||
'track_count' => count( $tracks ),
|
||||
'total_duration' => $duration,
|
||||
'duration_formatted' => $duration >= 3600
|
||||
? sprintf( '%d:%02d:%02d', floor( $duration / 3600 ), floor( ( $duration % 3600 ) / 60 ), $duration % 60 )
|
||||
: sprintf( '%d:%02d', floor( $duration / 60 ), $duration % 60 ),
|
||||
'tracks' => $tracks,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get taxonomy terms for post.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return array Terms with name and URL.
|
||||
*/
|
||||
private static function get_terms( int $post_id, string $taxonomy ): array {
|
||||
$terms = get_the_terms( $post_id, $taxonomy );
|
||||
|
||||
if ( ! $terms || is_wp_error( $terms ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_map(
|
||||
function ( $term ) {
|
||||
return array(
|
||||
'id' => $term->term_id,
|
||||
'name' => $term->name,
|
||||
'slug' => $term->slug,
|
||||
'url' => get_term_link( $term ),
|
||||
);
|
||||
},
|
||||
$terms
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archive posts.
|
||||
*
|
||||
* @return array Posts for archive.
|
||||
*/
|
||||
private static function get_archive_posts(): array {
|
||||
global $wp_query;
|
||||
|
||||
$posts = array();
|
||||
if ( $wp_query->have_posts() ) {
|
||||
while ( $wp_query->have_posts() ) {
|
||||
$wp_query->the_post();
|
||||
$posts[] = self::get_post_data( get_post() );
|
||||
}
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pagination data.
|
||||
*
|
||||
* @return array Pagination data.
|
||||
*/
|
||||
private static function get_pagination(): array {
|
||||
global $wp_query;
|
||||
|
||||
$total_pages = $wp_query->max_num_pages;
|
||||
$current = max( 1, get_query_var( 'paged' ) );
|
||||
|
||||
return array(
|
||||
'total_pages' => $total_pages,
|
||||
'current_page' => $current,
|
||||
'has_prev' => $current > 1,
|
||||
'has_next' => $current < $total_pages,
|
||||
'prev_url' => $current > 1 ? get_pagenum_link( $current - 1 ) : '',
|
||||
'next_url' => $current < $total_pages ? get_pagenum_link( $current + 1 ) : '',
|
||||
'links' => paginate_links(
|
||||
array(
|
||||
'total' => $total_pages,
|
||||
'current' => $current,
|
||||
'type' => 'array',
|
||||
'prev_text' => __( '« Previous', 'wp-fedistream' ),
|
||||
'next_text' => __( 'Next »', 'wp-fedistream' ),
|
||||
)
|
||||
) ?: array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archive title.
|
||||
*
|
||||
* @return string Archive title.
|
||||
*/
|
||||
private static function get_archive_title(): string {
|
||||
if ( is_post_type_archive( 'fedistream_artist' ) ) {
|
||||
return __( 'Artists', 'wp-fedistream' );
|
||||
}
|
||||
if ( is_post_type_archive( 'fedistream_album' ) ) {
|
||||
return __( 'Albums', 'wp-fedistream' );
|
||||
}
|
||||
if ( is_post_type_archive( 'fedistream_track' ) ) {
|
||||
return __( 'Tracks', 'wp-fedistream' );
|
||||
}
|
||||
if ( is_post_type_archive( 'fedistream_playlist' ) ) {
|
||||
return __( 'Playlists', 'wp-fedistream' );
|
||||
}
|
||||
if ( is_tax() ) {
|
||||
return single_term_title( '', false );
|
||||
}
|
||||
return get_the_archive_title();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archive description.
|
||||
*
|
||||
* @return string Archive description.
|
||||
*/
|
||||
private static function get_archive_description(): string {
|
||||
if ( is_tax() ) {
|
||||
return term_description();
|
||||
}
|
||||
return get_the_archive_description();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user