post = $post; } /** * Get the ActivityPub object type. * * @return string */ public function get_type(): string { return 'Audio'; } /** * Get the object ID (URI). * * @return string */ public function get_id(): string { return get_permalink( $this->post->ID ); } /** * Get the object name (title). * * @return string */ public function get_name(): string { return $this->post->post_title; } /** * Get the content (lyrics or description). * * @return string */ public function get_content(): string { $content = $this->post->post_content; // Apply content filters for proper formatting. $content = apply_filters( 'the_content', $content ); return wp_kses_post( $content ); } /** * Get the summary (excerpt). * * @return string */ public function get_summary(): string { if ( ! empty( $this->post->post_excerpt ) ) { return wp_strip_all_tags( $this->post->post_excerpt ); } // Generate excerpt from content. return wp_trim_words( wp_strip_all_tags( $this->post->post_content ), 30 ); } /** * Get the URL (permalink). * * @return string */ public function get_url(): string { return get_permalink( $this->post->ID ); } /** * Get the attributed actor(s). * * @return array|string Artist(s) URIs. */ public function get_attributed_to() { $artist_ids = get_post_meta( $this->post->ID, '_fedistream_artist_ids', true ); if ( ! is_array( $artist_ids ) || empty( $artist_ids ) ) { // Fall back to album artist. $album_id = get_post_meta( $this->post->ID, '_fedistream_album_id', true ); $artist_id = $album_id ? get_post_meta( $album_id, '_fedistream_album_artist', true ) : 0; if ( $artist_id ) { return get_permalink( $artist_id ); } return array(); } // Return single artist or array of artists. if ( count( $artist_ids ) === 1 ) { return get_permalink( $artist_ids[0] ); } return array_map( 'get_permalink', $artist_ids ); } /** * Get the published date. * * @return string ISO 8601 date. */ public function get_published(): string { return get_the_date( 'c', $this->post ); } /** * Get the updated date. * * @return string ISO 8601 date. */ public function get_updated(): string { return get_the_modified_date( 'c', $this->post ); } /** * Get the duration. * * @return string ISO 8601 duration. */ public function get_duration(): string { $seconds = (int) get_post_meta( $this->post->ID, '_fedistream_duration', true ); if ( ! $seconds ) { return ''; } return $this->format_duration_iso8601( $seconds ); } /** * Get the audio attachment. * * @return array|null */ public function get_audio_attachment(): ?array { $audio_id = get_post_meta( $this->post->ID, '_fedistream_audio_file', true ); if ( ! $audio_id ) { return null; } $audio_url = wp_get_attachment_url( $audio_id ); $mime_type = get_post_mime_type( $audio_id ); $audio_meta = wp_get_attachment_metadata( $audio_id ); if ( ! $audio_url ) { return null; } $attachment = array( 'type' => 'Audio', 'mediaType' => $mime_type ?: 'audio/mpeg', 'url' => $audio_url, 'name' => $this->post->post_title, ); // Add duration if available. $duration = $this->get_duration(); if ( $duration ) { $attachment['duration'] = $duration; } return $attachment; } /** * Get the image/thumbnail attachment. * * @return array|null */ public function get_image_attachment(): ?array { $thumbnail_id = get_post_thumbnail_id( $this->post->ID ); // Fall back to album artwork. if ( ! $thumbnail_id ) { $album_id = get_post_meta( $this->post->ID, '_fedistream_album_id', true ); $thumbnail_id = $album_id ? get_post_thumbnail_id( $album_id ) : 0; } if ( ! $thumbnail_id ) { return null; } $image = wp_get_attachment_image_src( $thumbnail_id, 'medium' ); if ( ! $image ) { return null; } return array( 'type' => 'Image', 'mediaType' => get_post_mime_type( $thumbnail_id ), 'url' => $image[0], 'width' => $image[1], 'height' => $image[2], ); } /** * Get tags (genres, moods). * * @return array */ public function get_tags(): array { $tags = array(); // Get genres. $genres = get_the_terms( $this->post->ID, 'fedistream_genre' ); if ( $genres && ! is_wp_error( $genres ) ) { foreach ( $genres as $genre ) { $tags[] = array( 'type' => 'Hashtag', 'name' => '#' . sanitize_title( $genre->name ), 'href' => get_term_link( $genre ), ); } } // Get moods. $moods = get_the_terms( $this->post->ID, 'fedistream_mood' ); if ( $moods && ! is_wp_error( $moods ) ) { foreach ( $moods as $mood ) { $tags[] = array( 'type' => 'Hashtag', 'name' => '#' . sanitize_title( $mood->name ), 'href' => get_term_link( $mood ), ); } } return $tags; } /** * Get the context (album). * * @return string|null Album URI if available. */ public function get_context(): ?string { $album_id = get_post_meta( $this->post->ID, '_fedistream_album_id', true ); if ( ! $album_id ) { return null; } return get_permalink( $album_id ); } /** * Transform to ActivityPub object array. * * @return array */ public function to_object(): array { $object = array( '@context' => 'https://www.w3.org/ns/activitystreams', 'type' => $this->get_type(), 'id' => $this->get_id(), 'name' => $this->get_name(), 'summary' => $this->get_summary(), 'content' => $this->get_content(), 'url' => $this->get_url(), 'attributedTo' => $this->get_attributed_to(), 'published' => $this->get_published(), 'updated' => $this->get_updated(), ); // Add duration. $duration = $this->get_duration(); if ( $duration ) { $object['duration'] = $duration; } // Add attachments. $attachments = array(); $audio = $this->get_audio_attachment(); if ( $audio ) { $attachments[] = $audio; } $image = $this->get_image_attachment(); if ( $image ) { $attachments[] = $image; } if ( ! empty( $attachments ) ) { $object['attachment'] = $attachments; } // Add tags. $tags = $this->get_tags(); if ( ! empty( $tags ) ) { $object['tag'] = $tags; } // Add context (album). $context = $this->get_context(); if ( $context ) { $object['context'] = $context; } // Add additional metadata. $explicit = get_post_meta( $this->post->ID, '_fedistream_explicit', true ); if ( $explicit ) { $object['sensitive'] = true; } // Add ISRC if available. $isrc = get_post_meta( $this->post->ID, '_fedistream_isrc', true ); if ( $isrc ) { $object['isrc'] = $isrc; } return $object; } /** * Create a Create activity for this track. * * @return array */ public function to_create_activity(): array { $attributed_to = $this->get_attributed_to(); $actor = is_array( $attributed_to ) ? $attributed_to[0] : $attributed_to; return array( '@context' => 'https://www.w3.org/ns/activitystreams', 'type' => 'Create', 'id' => $this->get_id() . '#activity-create', 'actor' => $actor, 'published' => $this->get_published(), 'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ), 'cc' => array( $actor . '/followers' ), 'object' => $this->to_object(), ); } /** * Create an Announce activity for this track. * * @param string $actor_uri The actor announcing the track. * @return array */ public function to_announce_activity( string $actor_uri ): array { return array( '@context' => 'https://www.w3.org/ns/activitystreams', 'type' => 'Announce', 'id' => $this->get_id() . '#activity-announce-' . time(), 'actor' => $actor_uri, 'published' => gmdate( 'c' ), 'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ), 'cc' => array( $actor_uri . '/followers' ), 'object' => $this->get_id(), ); } /** * Format duration as ISO 8601. * * @param int $seconds The duration in seconds. * @return string ISO 8601 duration. */ private function format_duration_iso8601( int $seconds ): string { $hours = floor( $seconds / 3600 ); $minutes = floor( ( $seconds % 3600 ) / 60 ); $secs = $seconds % 60; $duration = 'PT'; if ( $hours > 0 ) { $duration .= $hours . 'H'; } if ( $minutes > 0 ) { $duration .= $minutes . 'M'; } if ( $secs > 0 || ( $hours === 0 && $minutes === 0 ) ) { $duration .= $secs . 'S'; } return $duration; } }