5 Commits

Author SHA1 Message Date
eaefcff9c9 fix: Critical memory leak in TemplateLoader causing OOM errors
All checks were successful
Create Release Package / build-release (push) Successful in 57s
- Added recursion depth tracking to prevent infinite loops from shortcodes in content
- Nested items now skip the_content filter, using wp_kses_post() instead
- Made get_artist_data(), get_album_data(), get_track_data(), get_playlist_data() public
- Methods now accept both int post IDs and WP_Post objects
- Added $load_nested parameter to control nested item loading

Fixes memory exhaustion in Twig's StagingExtension when post content
contains FediStream shortcodes that trigger recursive template rendering.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:37:43 +01:00
04201a66f8 docs: Update session history with CI/CD fixes
Document all CI/CD pipeline fixes:
- Gitea API for releases (not GitHub action)
- Git submodule with relative URL
- Composer path repository
- gettext installation
- SIGPIPE fix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:46:06 +01:00
8ae703787c chore: Reorder exclusions in release workflow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:38:37 +01:00
c540cde0a4 docs: Update README for v0.4.0
- Update version badge to 0.4.0
- Add CI/CD badge
- Add release package installation instructions
- Add license key section
- Add releases section
- Update from-source instructions for submodules

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:35:06 +01:00
d96e3e3a4d chore: Exclude .gitea and .gitmodules from release package
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:33:54 +01:00
6 changed files with 233 additions and 75 deletions

View File

@@ -78,6 +78,7 @@ jobs:
-x "${PLUGIN_NAME}/.github/*" \ -x "${PLUGIN_NAME}/.github/*" \
-x "${PLUGIN_NAME}/.vscode/*" \ -x "${PLUGIN_NAME}/.vscode/*" \
-x "${PLUGIN_NAME}/.claude/*" \ -x "${PLUGIN_NAME}/.claude/*" \
-x "${PLUGIN_NAME}/.gitea/*" \
-x "${PLUGIN_NAME}/CLAUDE.md" \ -x "${PLUGIN_NAME}/CLAUDE.md" \
-x "${PLUGIN_NAME}/wp-core" \ -x "${PLUGIN_NAME}/wp-core" \
-x "${PLUGIN_NAME}/wp-core/*" \ -x "${PLUGIN_NAME}/wp-core/*" \
@@ -87,6 +88,7 @@ jobs:
-x "${PLUGIN_NAME}/composer.lock" \ -x "${PLUGIN_NAME}/composer.lock" \
-x "${PLUGIN_NAME}/*.log" \ -x "${PLUGIN_NAME}/*.log" \
-x "${PLUGIN_NAME}/.gitignore" \ -x "${PLUGIN_NAME}/.gitignore" \
-x "${PLUGIN_NAME}/.gitmodules" \
-x "${PLUGIN_NAME}/.editorconfig" \ -x "${PLUGIN_NAME}/.editorconfig" \
-x "${PLUGIN_NAME}/phpcs.xml*" \ -x "${PLUGIN_NAME}/phpcs.xml*" \
-x "${PLUGIN_NAME}/phpunit.xml*" \ -x "${PLUGIN_NAME}/phpunit.xml*" \

View File

@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.4.1] - 2026-02-02
### Fixed
- **Critical memory leak** causing "Allowed memory size exhausted" errors in Twig's StagingExtension
- Root cause: `apply_filters('the_content')` in `get_post_data()` triggered shortcode processing, causing infinite recursion when post content contained FediStream shortcodes
- Added recursion depth tracking with `MAX_RECURSION_DEPTH = 3` to prevent runaway nesting
- Nested items now skip `the_content` filter, using `wp_kses_post()` instead
- Nested data loading (albums within artists, tracks within albums) is now properly bounded
### Changed
- Made `get_artist_data()`, `get_album_data()`, `get_track_data()`, and `get_playlist_data()` public methods in TemplateLoader (previously private but called externally)
- These methods now accept both `int` post IDs and `WP_Post` objects for flexibility
- Added `$load_nested` parameter to control whether nested items are fully loaded or just counted
## [0.4.0] - 2026-01-29 ## [0.4.0] - 2026-01-29
### Added ### Added
@@ -191,7 +207,8 @@ Initial release of WP FediStream - a WordPress plugin for streaming music over A
--- ---
[Unreleased]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.4.0...HEAD [Unreleased]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.4.1...HEAD
[0.4.1]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.4.0...v0.4.1
[0.4.0]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.3.0...v0.4.0 [0.4.0]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.3.0...v0.4.0
[0.3.0]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.2.0...v0.3.0 [0.3.0]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.2.0...v0.3.0
[0.2.0]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.1.1...v0.2.0 [0.2.0]: https://src.bundespruefstelle.ch/magdev/wp-fedistream/compare/v0.1.1...v0.2.0

View File

@@ -507,7 +507,7 @@ wp-fedistream/
### 2026-01-29 - CI/CD Pipeline v0.4.0 ### 2026-01-29 - CI/CD Pipeline v0.4.0
**Summary:** Added Gitea Actions workflow for automated release package creation. **Summary:** Added Gitea Actions workflow for automated release package creation with multiple iterations to fix CI issues.
**Features:** **Features:**
@@ -520,23 +520,35 @@ wp-fedistream/
- SHA256 checksum generation - SHA256 checksum generation
- Package structure verification - Package structure verification
- Changelog extraction for release notes - Changelog extraction for release notes
- Automatic Gitea release creation with attachments - Automatic Gitea release creation via API
- Pre-release detection for tags containing `-` - Pre-release detection for tags containing `-`
**Files Created:** **Files Created:**
- `.gitea/workflows/release.yml` - CI/CD release pipeline - `.gitea/workflows/release.yml` - CI/CD release pipeline
- `.gitmodules` - Git submodule configuration
- `lib/wc-licensed-product-client/` - Submodule for private dependency
**Files Modified:** **Files Modified:**
- `CLAUDE.md` - Added CI/CD documentation and updated directory structure - `CLAUDE.md` - Added CI/CD documentation and updated directory structure
- `CHANGELOG.md` - Added v0.4.0 entry - `CHANGELOG.md` - Added v0.4.0 entry
- `wp-fedistream.php` - Version bump to 0.4.0 - `wp-fedistream.php` - Version bump to 0.4.0
- `composer.json` - Changed to path repository for submodule
- `README.md` - Updated for v0.4.0, added release/installation docs
**CI/CD Fixes Applied:**
1. `actions/gitea-release-action@v1` doesn't exist - use Gitea API directly with curl
2. Private repo network issue - use git submodule with relative URL (`../wc-licensed-product-client.git`)
3. Composer path repository for submodule dependency
4. `msgfmt` not found - install gettext package
5. SIGPIPE error (exit 141) - use `set +o pipefail` and `|| true`
**Notes:** **Notes:**
- Requires `GITEA_TOKEN` secret configured in repository settings - Requires `SRC_GITEA_TOKEN` secret configured in repository settings
- Uses `shivammathur/setup-php@v2` for PHP setup - Uses `shivammathur/setup-php@v2` for PHP setup
- Uses `actions/gitea-release-action@v1` for release creation - Uses Gitea API directly for release creation (not GitHub Actions)
- Compatible with GitHub Actions syntax - Submodule uses relative URL for CI compatibility
- User simplified checksums to SHA256 only (removed MD5) - Composer symlinks from `lib/wc-licensed-product-client` to vendor

View File

@@ -2,10 +2,11 @@
Stream music over ActivityPub - Build your own music streaming platform for Musicians and Labels. Stream music over ActivityPub - Build your own music streaming platform for Musicians and Labels.
[![Version](https://img.shields.io/badge/version-0.2.0-blue.svg)](CHANGELOG.md) [![Version](https://img.shields.io/badge/version-0.4.0-blue.svg)](CHANGELOG.md)
[![PHP](https://img.shields.io/badge/PHP-%3E%3D8.3-purple.svg)](https://php.net) [![PHP](https://img.shields.io/badge/PHP-%3E%3D8.3-purple.svg)](https://php.net)
[![WordPress](https://img.shields.io/badge/WordPress-%3E%3D6.4-blue.svg)](https://wordpress.org) [![WordPress](https://img.shields.io/badge/WordPress-%3E%3D6.4-blue.svg)](https://wordpress.org)
[![License](https://img.shields.io/badge/license-GPL--2.0%2B-green.svg)](https://www.gnu.org/licenses/gpl-2.0.html) [![License](https://img.shields.io/badge/license-GPL--2.0%2B-green.svg)](https://www.gnu.org/licenses/gpl-2.0.html)
[![CI/CD](https://img.shields.io/badge/CI%2FCD-Gitea%20Actions-green.svg)](https://src.bundespruefstelle.ch/magdev/wp-fedistream/actions)
## Description ## Description
@@ -31,29 +32,47 @@ WP FediStream is a WordPress plugin that enables musicians, bands, and labels to
- PHP 8.3 or higher - PHP 8.3 or higher
- WordPress 6.4 or higher - WordPress 6.4 or higher
- Composer (for development/installation) - Valid license key (required for frontend features)
### Optional ### Optional
- [ActivityPub Plugin](https://wordpress.org/plugins/activitypub/) - For Fediverse integration - [ActivityPub Plugin](https://wordpress.org/plugins/activitypub/) - For Fediverse integration
- [WooCommerce](https://woocommerce.com/) 10.0+ - For selling music - [WooCommerce](https://woocommerce.com/) 10.0+ - For selling music
## License Key
WP FediStream requires a valid license key for frontend functionality (player, shortcodes, ActivityPub). The admin dashboard works without a license, allowing you to configure the plugin before activation.
To obtain a license key, contact the author or purchase from the official website.
## Installation ## Installation
### From Source ### From Release Package (Recommended)
1. Clone or download the repository to your WordPress plugins directory: 1. Download the latest release from the [Releases page](https://src.bundespruefstelle.ch/magdev/wp-fedistream/releases)
2. Upload the ZIP file via **Plugins > Add New > Upload Plugin** in WordPress admin
3. Activate the plugin under **Plugins > Installed Plugins**
4. Navigate to **FediStream > Settings** and enter your license key
5. Start using the plugin via the **FediStream** admin menu
### From Source (Development)
1. Clone the repository to your WordPress plugins directory:
```bash ```bash
cd wp-content/plugins/ cd wp-content/plugins/
git clone https://src.bundespruefstelle.ch/magdev/wp-fedistream.git git clone --recurse-submodules https://src.bundespruefstelle.ch/magdev/wp-fedistream.git
``` ```
2. Install Composer dependencies: 2. Install Composer dependencies:
```bash ```bash
cd wp-fedistream cd wp-fedistream
composer install --no-dev composer install
``` ```
3. Activate the plugin in WordPress admin under **Plugins > Installed Plugins** 3. Activate the plugin in WordPress admin under **Plugins > Installed Plugins**
@@ -133,6 +152,16 @@ wp-fedistream/
└── wp-fedistream.php # Plugin entry point └── wp-fedistream.php # Plugin entry point
``` ```
## Releases
Release packages are automatically built via Gitea Actions when a version tag is pushed. Each release includes:
- Production-ready ZIP package with all dependencies
- SHA256 checksum for verification
- Changelog notes extracted from CHANGELOG.md
Download releases from: <https://src.bundespruefstelle.ch/magdev/wp-fedistream/releases>
## Contributing ## Contributing
This project is in early development. Contributions, bug reports, and feature requests are welcome. This project is in early development. Contributions, bug reports, and feature requests are welcome.

View File

@@ -21,6 +21,20 @@ if ( ! defined( 'ABSPATH' ) ) {
*/ */
class TemplateLoader { class TemplateLoader {
/**
* Recursion depth for get_post_data calls.
*
* @var int
*/
private static int $recursion_depth = 0;
/**
* Maximum allowed recursion depth.
*
* @var int
*/
private const MAX_RECURSION_DEPTH = 3;
/** /**
* Constructor. * Constructor.
*/ */
@@ -191,14 +205,21 @@ class TemplateLoader {
/** /**
* Get post data for template. * Get post data for template.
* *
* @param \WP_Post $post Post object. * @param \WP_Post $post Post object.
* @param bool $skip_nested Whether to skip loading nested items (albums, tracks, etc.).
* @return array Post data. * @return array Post data.
*/ */
public static function get_post_data( \WP_Post $post ): array { public static function get_post_data( \WP_Post $post, bool $skip_nested = false ): array {
// Track recursion to prevent infinite loops from shortcodes in content.
++self::$recursion_depth;
// At depth > 1, skip the_content filter to prevent shortcode recursion.
$is_nested = self::$recursion_depth > 1;
$data = array( $data = array(
'id' => $post->ID, 'id' => $post->ID,
'title' => get_the_title( $post ), 'title' => get_the_title( $post ),
'content' => apply_filters( 'the_content', $post->post_content ), 'content' => $is_nested ? wp_kses_post( $post->post_content ) : apply_filters( 'the_content', $post->post_content ),
'excerpt' => get_the_excerpt( $post ), 'excerpt' => get_the_excerpt( $post ),
'permalink' => get_permalink( $post ), 'permalink' => get_permalink( $post ),
'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'large' ), 'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'large' ),
@@ -206,19 +227,21 @@ class TemplateLoader {
'author' => get_the_author_meta( 'display_name', $post->post_author ), 'author' => get_the_author_meta( 'display_name', $post->post_author ),
); );
// Add post type specific data. // Add post type specific data (skip nested items if at max depth).
$load_nested = ! $skip_nested && self::$recursion_depth < self::MAX_RECURSION_DEPTH;
switch ( $post->post_type ) { switch ( $post->post_type ) {
case 'fedistream_artist': case 'fedistream_artist':
$data = array_merge( $data, self::get_artist_data( $post->ID ) ); $data = array_merge( $data, self::get_artist_data( $post->ID, $load_nested ) );
break; break;
case 'fedistream_album': case 'fedistream_album':
$data = array_merge( $data, self::get_album_data( $post->ID ) ); $data = array_merge( $data, self::get_album_data( $post->ID, $load_nested ) );
break; break;
case 'fedistream_track': case 'fedistream_track':
$data = array_merge( $data, self::get_track_data( $post->ID ) ); $data = array_merge( $data, self::get_track_data( $post->ID ) );
break; break;
case 'fedistream_playlist': case 'fedistream_playlist':
$data = array_merge( $data, self::get_playlist_data( $post->ID ) ); $data = array_merge( $data, self::get_playlist_data( $post->ID, $load_nested ) );
break; break;
} }
@@ -226,16 +249,23 @@ class TemplateLoader {
$data['genres'] = self::get_terms( $post->ID, 'fedistream_genre' ); $data['genres'] = self::get_terms( $post->ID, 'fedistream_genre' );
$data['moods'] = self::get_terms( $post->ID, 'fedistream_mood' ); $data['moods'] = self::get_terms( $post->ID, 'fedistream_mood' );
--self::$recursion_depth;
return $data; return $data;
} }
/** /**
* Get artist-specific data. * Get artist-specific data.
* *
* @param int $post_id Post ID. * @param int|\WP_Post $post_id Post ID or WP_Post object.
* @param bool $load_nested Whether to load nested albums.
* @return array Artist data. * @return array Artist data.
*/ */
private static function get_artist_data( int $post_id ): array { public static function get_artist_data( int|\WP_Post $post_id, bool $load_nested = true ): array {
// Support both post ID and WP_Post object.
if ( $post_id instanceof \WP_Post ) {
$post_id = $post_id->ID;
}
$type = get_post_meta( $post_id, '_fedistream_artist_type', true ) ?: 'solo'; $type = get_post_meta( $post_id, '_fedistream_artist_type', true ) ?: 'solo';
$types = array( $types = array(
'solo' => __( 'Solo Artist', 'wp-fedistream' ), 'solo' => __( 'Solo Artist', 'wp-fedistream' ),
@@ -244,23 +274,48 @@ class TemplateLoader {
'collective' => __( 'Collective', 'wp-fedistream' ), 'collective' => __( 'Collective', 'wp-fedistream' ),
); );
$albums = get_posts( $albums = array();
array( $album_count = 0;
'post_type' => 'fedistream_album',
'posts_per_page' => -1, if ( $load_nested ) {
'post_status' => 'publish', $album_posts = get_posts(
'meta_key' => '_fedistream_album_artist', array(
'meta_value' => $post_id, 'post_type' => 'fedistream_album',
'orderby' => 'meta_value', 'posts_per_page' => -1,
'meta_query' => array( 'post_status' => 'publish',
array( 'meta_key' => '_fedistream_album_artist',
'key' => '_fedistream_album_release_date', 'meta_value' => $post_id,
'compare' => 'EXISTS', 'orderby' => 'meta_value',
'meta_query' => array(
array(
'key' => '_fedistream_album_release_date',
'compare' => 'EXISTS',
),
), ),
), 'order' => 'DESC',
'order' => 'DESC', )
) );
); $album_count = count( $album_posts );
$albums = array_map(
function ( $album ) {
return self::get_post_data( $album, true ); // Skip further nesting.
},
$album_posts
);
} else {
// Just get the count without loading full data.
$album_count = (int) get_posts(
array(
'post_type' => 'fedistream_album',
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_key' => '_fedistream_album_artist',
'meta_value' => $post_id,
'fields' => 'ids',
)
);
$album_count = is_array( $album_count ) ? count( $album_count ) : 0;
}
return array( return array(
'artist_type' => $type, 'artist_type' => $type,
@@ -270,18 +325,23 @@ class TemplateLoader {
'website' => get_post_meta( $post_id, '_fedistream_artist_website', true ), 'website' => get_post_meta( $post_id, '_fedistream_artist_website', true ),
'social_links' => get_post_meta( $post_id, '_fedistream_artist_social_links', true ) ?: array(), 'social_links' => get_post_meta( $post_id, '_fedistream_artist_social_links', true ) ?: array(),
'members' => get_post_meta( $post_id, '_fedistream_artist_members', true ) ?: array(), 'members' => get_post_meta( $post_id, '_fedistream_artist_members', true ) ?: array(),
'albums' => array_map( array( __CLASS__, 'get_post_data' ), $albums ), 'albums' => $albums,
'album_count' => count( $albums ), 'album_count' => $album_count,
); );
} }
/** /**
* Get album-specific data. * Get album-specific data.
* *
* @param int $post_id Post ID. * @param int|\WP_Post $post_id Post ID or WP_Post object.
* @param bool $load_nested Whether to load nested tracks.
* @return array Album data. * @return array Album data.
*/ */
private static function get_album_data( int $post_id ): array { public static function get_album_data( int|\WP_Post $post_id, bool $load_nested = true ): array {
// Support both post ID and WP_Post object.
if ( $post_id instanceof \WP_Post ) {
$post_id = $post_id->ID;
}
$type = get_post_meta( $post_id, '_fedistream_album_type', true ) ?: 'album'; $type = get_post_meta( $post_id, '_fedistream_album_type', true ) ?: 'album';
$types = array( $types = array(
'album' => __( 'Album', 'wp-fedistream' ), 'album' => __( 'Album', 'wp-fedistream' ),
@@ -293,24 +353,49 @@ class TemplateLoader {
); );
$artist_id = get_post_meta( $post_id, '_fedistream_album_artist', true ); $artist_id = get_post_meta( $post_id, '_fedistream_album_artist', true );
$tracks = get_posts( $tracks = array();
array( $total_tracks = 0;
'post_type' => 'fedistream_track',
'posts_per_page' => -1, if ( $load_nested ) {
'post_status' => 'publish', $track_posts = get_posts(
'meta_key' => '_fedistream_track_album', array(
'meta_value' => $post_id, 'post_type' => 'fedistream_track',
'orderby' => 'meta_value_num', 'posts_per_page' => -1,
'meta_query' => array( 'post_status' => 'publish',
'relation' => 'AND', 'meta_key' => '_fedistream_track_album',
array( 'meta_value' => $post_id,
'key' => '_fedistream_track_number', 'orderby' => 'meta_value_num',
'compare' => 'EXISTS', 'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_fedistream_track_number',
'compare' => 'EXISTS',
),
), ),
), 'order' => 'ASC',
'order' => 'ASC', )
) );
); $total_tracks = count( $track_posts );
$tracks = array_map(
function ( $track ) {
return self::get_post_data( $track, true ); // Skip further nesting.
},
$track_posts
);
} else {
// Just get the count without loading full data.
$track_ids = get_posts(
array(
'post_type' => 'fedistream_track',
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_key' => '_fedistream_track_album',
'meta_value' => $post_id,
'fields' => 'ids',
)
);
$total_tracks = is_array( $track_ids ) ? count( $track_ids ) : 0;
}
return array( return array(
'album_type' => $type, 'album_type' => $type,
@@ -322,19 +407,23 @@ class TemplateLoader {
'artist_url' => $artist_id ? get_permalink( $artist_id ) : '', 'artist_url' => $artist_id ? get_permalink( $artist_id ) : '',
'upc' => get_post_meta( $post_id, '_fedistream_album_upc', true ), 'upc' => get_post_meta( $post_id, '_fedistream_album_upc', true ),
'catalog_number' => get_post_meta( $post_id, '_fedistream_album_catalog_number', true ), 'catalog_number' => get_post_meta( $post_id, '_fedistream_album_catalog_number', true ),
'total_tracks' => count( $tracks ), 'total_tracks' => $total_tracks,
'total_duration' => (int) get_post_meta( $post_id, '_fedistream_album_total_duration', true ), 'total_duration' => (int) get_post_meta( $post_id, '_fedistream_album_total_duration', true ),
'tracks' => array_map( array( __CLASS__, 'get_post_data' ), $tracks ), 'tracks' => $tracks,
); );
} }
/** /**
* Get track-specific data. * Get track-specific data.
* *
* @param int $post_id Post ID. * @param int|\WP_Post $post_id Post ID or WP_Post object.
* @return array Track data. * @return array Track data.
*/ */
private static function get_track_data( int $post_id ): array { public static function get_track_data( int|\WP_Post $post_id ): array {
// Support both post ID and WP_Post object.
if ( $post_id instanceof \WP_Post ) {
$post_id = $post_id->ID;
}
$album_id = get_post_meta( $post_id, '_fedistream_track_album', true ); $album_id = get_post_meta( $post_id, '_fedistream_track_album', true );
$audio_file = get_post_meta( $post_id, '_fedistream_track_audio_file', true ); $audio_file = get_post_meta( $post_id, '_fedistream_track_audio_file', true );
$artists = get_post_meta( $post_id, '_fedistream_track_artists', true ) ?: array(); $artists = get_post_meta( $post_id, '_fedistream_track_artists', true ) ?: array();
@@ -374,16 +463,21 @@ class TemplateLoader {
/** /**
* Get playlist-specific data. * Get playlist-specific data.
* *
* @param int $post_id Post ID. * @param int|\WP_Post $post_id Post ID or WP_Post object.
* @param bool $load_nested Whether to load nested tracks.
* @return array Playlist data. * @return array Playlist data.
*/ */
private static function get_playlist_data( int $post_id ): array { public static function get_playlist_data( int|\WP_Post $post_id, bool $load_nested = true ): array {
// Support both post ID and WP_Post object.
if ( $post_id instanceof \WP_Post ) {
$post_id = $post_id->ID;
}
global $wpdb; global $wpdb;
$table = $wpdb->prefix . 'fedistream_playlist_tracks'; $table = $wpdb->prefix . 'fedistream_playlist_tracks';
$duration = (int) get_post_meta( $post_id, '_fedistream_playlist_total_duration', true ); $duration = (int) get_post_meta( $post_id, '_fedistream_playlist_total_duration', true );
// Get tracks. // Get track IDs.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$track_ids = $wpdb->get_col( $track_ids = $wpdb->get_col(
$wpdb->prepare( $wpdb->prepare(
@@ -392,11 +486,15 @@ class TemplateLoader {
) )
); );
$tracks = array(); $tracks = array();
foreach ( $track_ids as $track_id ) { $track_count = count( $track_ids );
$track = get_post( $track_id );
if ( $track && 'publish' === $track->post_status ) { if ( $load_nested && ! empty( $track_ids ) ) {
$tracks[] = self::get_post_data( $track ); foreach ( $track_ids as $track_id ) {
$track = get_post( $track_id );
if ( $track && 'publish' === $track->post_status ) {
$tracks[] = self::get_post_data( $track, true ); // Skip further nesting.
}
} }
} }
@@ -404,7 +502,7 @@ class TemplateLoader {
'visibility' => get_post_meta( $post_id, '_fedistream_playlist_visibility', true ) ?: 'public', 'visibility' => get_post_meta( $post_id, '_fedistream_playlist_visibility', true ) ?: 'public',
'collaborative' => (bool) get_post_meta( $post_id, '_fedistream_playlist_collaborative', true ), 'collaborative' => (bool) get_post_meta( $post_id, '_fedistream_playlist_collaborative', true ),
'federated' => (bool) get_post_meta( $post_id, '_fedistream_playlist_federated', true ), 'federated' => (bool) get_post_meta( $post_id, '_fedistream_playlist_federated', true ),
'track_count' => count( $tracks ), 'track_count' => $load_nested ? count( $tracks ) : $track_count,
'total_duration' => $duration, 'total_duration' => $duration,
'duration_formatted' => $duration >= 3600 'duration_formatted' => $duration >= 3600
? sprintf( '%d:%02d:%02d', floor( $duration / 3600 ), floor( ( $duration % 3600 ) / 60 ), $duration % 60 ) ? sprintf( '%d:%02d:%02d', floor( $duration / 3600 ), floor( ( $duration % 3600 ) / 60 ), $duration % 60 )

View File

@@ -3,7 +3,7 @@
* Plugin Name: WP FediStream * Plugin Name: WP FediStream
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-fedistream * Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-fedistream
* Description: Stream music over ActivityPub - Build your own music streaming platform for Musicians and Labels. * Description: Stream music over ActivityPub - Build your own music streaming platform for Musicians and Labels.
* Version: 0.4.0 * Version: 0.4.1
* Requires at least: 6.4 * Requires at least: 6.4
* Requires PHP: 8.3 * Requires PHP: 8.3
* Author: Marco Graetsch * Author: Marco Graetsch
@@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* *
* @var string * @var string
*/ */
define( 'WP_FEDISTREAM_VERSION', '0.4.0' ); define( 'WP_FEDISTREAM_VERSION', '0.4.1' );
/** /**
* Plugin file path. * Plugin file path.