# Wordpress Plugin to stream music over Activity Pub **Author:** Marco Graetsch **Author URL:** **Author Email:** **Repository URL:** **Issues URL:** ## Project Overview This plugin provides a way for Musicians or Music-Labels to cut the ties with Spotify, Youtube Music et al and build their own Streaming-Platform (optionally including selling their work using WooCommerce) base on the ActivityPub protocol. This plugin implements the management of Musicians (Single or Bands), Albums (or Releases), Tracks and Playlists. The plugin utilizes the ActivityPub protocol to publish Releases or Tracks, share Playlists and let users create Playlist from different Wordpress instances, which use this plugin. It also employs Fediverse reactions to build comprehensive profile for Musicians and Bands (or the Label at all), including classicla Blogposts from Musicians. The Plugin should serve as a full Fediverse profile for Musicians. Everyone in the Fediverse is able to subscribe to the Label's or Musician's account. If WooCommerce is installed, the Musician (or the Label) is allowed to sell the Music either as Album or single track utilizing special WooCommerce product types. The goal is to create an alternative to the big tech streaming plaforms and give the musicians their freedom back and concentrate to make music. The first goal is to publish, the second goal is to monetize the platform without ripping of the users or musicians. ### Key Fact: 100% AI-Generated This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance. ## Temporary Roadmap **Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file. (No pending features - all roadmap items completed) ## Technical Stack - **Language:** PHP 8.3.x - **Framework:** Latest WordPress Plugin API - **E-commerce (optional):** WooCommerce 10.0+ - **Template Engine:** Native PHP templates (with theme override support) - **Communication Protocol:** ActivityPub - **Wordpress Base Theme** twentytwentyfive - **Frontend:** Vanilla JavaScript - **Styling:** Custom CSS - **Dependency Management:** Composer - **Internationalization:** WordPress i18n (.pot/.po/.mo files) - **Canonical Plugin Name:** `wp-fedistream` ### Security Best Practices - All user inputs are sanitized (integers for quantities/prices) - Nonce verification on form submissions - Output escaping in templates (`esc_attr`, `esc_html`, `esc_js`) - Direct file access prevention via `ABSPATH` check - XSS-safe DOM construction in JavaScript (no `innerHTML` with user data) - SQL injection prevention using `$wpdb->prepare()` throughout ### Translation Ready All user-facing strings use: ```php __('Text to translate', 'wp-fedistream') _e('Text to translate', 'wp-fedistream') ``` Text domain: `wp-fedistream` #### Translation Template - Base `.pot` file created: `languages/wp-fedistream.pot` - Ready for translation to any locale - All translatable strings properly marked with text domain #### Available Translations - `en_US` - English (United States) [base language - .pot template] - `de_CH` - German (Switzerland, formal) To compile translations to .mo files for production: ```bash for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done ``` ### Create releases - The `vendor/` directory MUST be included in releases (Dependencies required for runtime) - **Don't create any release files until version 0.1.x and up!** - **CRITICAL**: Build `vendor/` for the MINIMUM supported PHP version, not the development version - Use `composer config platform.php 8.3.0` before building release packages - Run `composer update --no-dev --optimize-autoloader` to rebuild dependencies - **CRITICAL**: WordPress requires plugins in a subdirectory structure - Run zip from the `plugins/` parent directory, NOT from within the plugin directory - Package must extract to `wp-fedistream/` subdirectory with main file at `wp-fedistream/wp-fedistream.php` - Correct command: `cd /wp-content/plugins/ && zip -r wp-fedistream/releases/wp-fedistream-x.x.x.zip wp-fedistream ...` - Wrong: Running zip from inside the plugin directory creates files at root level - **CRITICAL**: Exclude symlinks explicitly - zip follows symlinks by default - Always use `-x "wp-fedistream/wp-core" -x "wp-fedistream/wp-core/*" -x "wp-fedistream/wp-plugins" -x "wp-fedistream/wp-plugins/*"` to exclude development symlinks - Otherwise the entire linked directory contents will be included in the package - Exclusion patterns must match the relative path structure used in zip command - Always verify the package structure with `unzip -l` before distribution - Check all files are prefixed with `wp-fedistream/` - Verify main file is at `wp-fedistream/wp-fedistream.php` - Check for duplicate entries (indicates multiple builds in same archive) - Test installation on the minimum supported PHP version before final deployment - Releases are stored in `releases/` including checksums - Track release changes in a single `CHANGELOG.md` file - Bump the version number to either bugfix release versions or on new features minor release versions - **CRITICAL**: WordPress reads version from TWO places - BOTH must be updated: 1. Plugin header comment `Version: x.x.x` (line ~6 in wc-licensed-product.php) - WordPress uses THIS for admin display 2. PHP constant `WP_FEDISTREAM_VERSION` (line ~28) - Used internally by the plugin - If only the constant is updated, WordPress will show the old version in Plugins list **Important Git Notes:** - Default branch while development is `dev` - Create releases from branch `main` after merging branch `dev` - Tags should use format `vX.X.X` (e.g., `v1.1.22`), start with v0.1.0 - Use annotated tags (`-a`) not lightweight tags - Commit messages should follow the established format with Claude Code attribution - `.claude/settings.local.json` changes are typically local-only (stash before rebasing) #### What Gets Released - All plugin source files - Compiled vendor dependencies - Translation files (.mo compiled from .po) - Assets (CSS, JS) - Documentation (README, CHANGELOG, etc.) #### What's Excluded - Git metadata (`.git/`) - Development files (`.vscode/`, `.claude/`, `.gitea/`, `CLAUDE.md`, `wp-core`, `wp-plugins`) - Logs and cache files - Previous releases - `composer.lock` (but `vendor/` is included) #### CI/CD Pipeline (Gitea Actions) Automated release packages are created via Gitea Actions when a tag matching `v*` is pushed: **Workflow:** `.gitea/workflows/release.yml` **Trigger:** Push tag `vX.X.X` to repository **Steps:** 1. Checkout code 2. Setup PHP 8.3 with required extensions 3. Install production Composer dependencies 4. Compile translations (.po to .mo) 5. Verify plugin version matches tag version 6. Build release zip with proper WordPress structure 7. Generate SHA256 checksums 8. Verify package structure 9. Extract changelog for release notes 10. Create Gitea release with attachments **Required Secret:** `GITEA_TOKEN` - Personal access token with release permissions **Pre-release Detection:** Tags containing `-` (e.g., `v1.0.0-beta`) are marked as pre-release **To create a release:** ```bash # Ensure version is updated in wp-fedistream.php (both header and constant) git checkout main git merge dev git tag -a v0.4.0 -m "Release v0.4.0" git push origin main --tags ``` The pipeline will automatically build and publish the release package. --- **For AI Assistants:** When starting a new session on this project: 1. Read this CLAUDE.md file first 2. Semantic versioning follows the `MAJOR.MINOR.BUGFIX` pattern 3. Check git log for recent changes 4. Verify you're on the `dev` branch before making changes 5. Run `composer install` if vendor/ is missing 6. Test changes before committing 7. Follow commit message format with Claude Code attribution 8. Update this session history section with learnings 9. Never commit backup files (`*.po~`, `*.bak`, etc.) - check `git status` before committing 10. Follow markdown linting rules (see below) Always refer to this document when starting work on this project. ### Markdown Linting Rules When editing CLAUDE.md or other markdown files, follow these rules to avoid linting errors: 1. **MD031 - Blank lines around fenced code blocks**: Always add a blank line before and after fenced code blocks, even when they follow list items. Example of correct format: - **Item label**: (blank line here) \`\`\`php code example \`\`\` (blank line here) 2. **MD056 - Table column count**: Table separators must have matching column counts and proper spacing. Use consistent dash lengths that match column header widths. 3. **MD009 - No trailing spaces**: Remove trailing whitespace from lines 4. **MD012 - No multiple consecutive blank lines**: Use only single blank lines between sections 5. **MD040 - Fenced code blocks should have a language specified**: Always add a language identifier to code blocks (e.g., `txt`, `bash`, `php`). For shortcode examples, use `txt`. 6. **MD032 - Lists should be surrounded by blank lines**: Add a blank line before AND after list blocks, including after bold labels like `**Attributes:**`. 7. **MD034 - Bare URLs**: Wrap URLs in angle brackets (e.g., ``) or use markdown link syntax `[text](url)`. 8. **Author section formatting**: Use a heading (`### Name`) instead of bold (`**Name**`) for the author name to maintain consistent document structure. ## Project Architecture ### Directory Structure ```txt wp-fedistream/ ├── .gitea/ │ └── workflows/ │ └── release.yml # CI/CD release pipeline ├── assets/ │ ├── css/ │ │ ├── admin.css # Admin interface styles │ │ └── frontend.css # Frontend styles │ ├── js/ │ │ ├── admin.js # Admin interface scripts │ │ ├── frontend.js # Frontend scripts │ │ ├── player.js # Audio player │ │ ├── library.js # User library page │ │ └── notifications.js # Notification system │ └── images/ ├── includes/ │ ├── ActivityPub/ │ │ ├── Integration.php # ActivityPub plugin integration │ │ ├── ArtistActor.php # Artist as ActivityPub actor │ │ ├── Inbox.php # Process incoming activities │ │ ├── Outbox.php # Publish outgoing activities │ │ ├── RestApi.php # REST endpoints for ActivityPub │ │ └── Transformers/ # Object transformers │ │ ├── TrackTransformer.php │ │ ├── AlbumTransformer.php │ │ └── PlaylistTransformer.php │ ├── Admin/ │ │ └── ListColumns.php # Custom list table columns │ ├── Frontend/ │ │ ├── Ajax.php # AJAX handlers │ │ ├── Shortcodes.php # All shortcodes │ │ ├── TemplateLoader.php # Template loading │ │ └── Widgets.php # Widget registration │ ├── PostTypes/ │ │ ├── Artist.php # fedistream_artist │ │ ├── Album.php # fedistream_album │ │ ├── Track.php # fedistream_track │ │ └── Playlist.php # fedistream_playlist │ ├── Roles/ │ │ └── Capabilities.php # User roles and caps │ ├── Taxonomies/ │ │ ├── Genre.php # fedistream_genre │ │ ├── Mood.php # fedistream_mood │ │ └── License.php # fedistream_license │ ├── User/ │ │ ├── Library.php # Favorites, follows, history │ │ ├── LibraryPage.php # Library shortcode │ │ └── Notifications.php # Notification system │ ├── WooCommerce/ │ │ ├── Integration.php # WooCommerce setup │ │ ├── AlbumProduct.php # Album product type │ │ ├── TrackProduct.php # Track product type │ │ ├── DigitalDelivery.php # Download handling │ │ └── StreamingAccess.php # Access control │ ├── Installer.php # Database setup, activation │ └── Plugin.php # Main singleton class ├── languages/ │ ├── wp-fedistream.pot # Translation template │ └── wp-fedistream-de_CH.po # German (Switzerland) ├── templates/ # Twig templates │ ├── admin/ │ ├── archive/ │ ├── single/ │ └── player/ ├── vendor/ # Composer dependencies ├── composer.json ├── uninstall.php └── wp-fedistream.php # Plugin entry point ``` ### Database Tables | Table | Purpose | | ----- | ------- | | `{prefix}_fedistream_plays` | Track play statistics | | `{prefix}_fedistream_playlist_tracks` | Playlist-track relationships | | `{prefix}_fedistream_followers` | ActivityPub followers | | `{prefix}_fedistream_purchases` | WooCommerce purchase tracking | | `{prefix}_fedistream_favorites` | User favorites | | `{prefix}_fedistream_user_follows` | Local artist follows | | `{prefix}_fedistream_listening_history` | Track play history | | `{prefix}_fedistream_notifications` | User notifications | | `{prefix}_fedistream_reactions` | Fediverse reactions | ### Custom Post Types | Post Type | Slug | Description | | --------- | ---- | ----------- | | `fedistream_artist` | `/artists/` | Musicians, bands, collectives | | `fedistream_album` | `/albums/` | Albums, EPs, singles, compilations | | `fedistream_track` | `/tracks/` | Individual audio tracks | | `fedistream_playlist` | `/playlists/` | Curated track collections | ### Custom Taxonomies | Taxonomy | Type | Applied To | | -------- | ---- | ---------- | | `fedistream_genre` | Hierarchical | Artists, Albums, Tracks | | `fedistream_mood` | Non-hierarchical | Tracks, Playlists | | `fedistream_license` | Hierarchical | Albums, Tracks | ### User Roles | Role | Slug | Capabilities | | ---- | ---- | ------------ | | Artist | `fedistream_artist` | Manage own content, upload files | | Label | `fedistream_label` | Manage all content, taxonomies, stats | ### Shortcodes | Shortcode | Description | | --------- | ----------- | | `[fedistream_player track_id="123"]` | Single track player | | `[fedistream_playlist id="456"]` | Playlist display | | `[fedistream_album id="789"]` | Album display | | `[fedistream_artist id="101"]` | Artist profile | | `[fedistream_recent_releases count="5"]` | Recent releases | | `[fedistream_popular_tracks count="10"]` | Popular tracks | | `[fedistream_library]` | User library page | ### Key Classes - `Plugin` (singleton) - Main controller, initializes all components - `Installer` - Database setup, activation/deactivation hooks - `Artist`, `Album`, `Track`, `Playlist` - Post type registration and meta boxes - `Genre`, `Mood`, `License` - Taxonomy registration with defaults - `Capabilities` - User role and capability management - `ActivityPubIntegration` - Integration with WordPress ActivityPub plugin - `ArtistActor` - Artist profiles as ActivityPub Person/Group actors - `Inbox` - Process Follow, Like, Announce, Create activities - `Outbox` - Publish Create, Update activities to followers - `WooCommerceIntegration` - Custom product types for albums/tracks - `DigitalDelivery` - Secure download handling with ZIP support - `StreamingAccess` - Purchase-based streaming control - `Library` - User favorites, follows, listening history - `Notifications` - In-app and email notification system ### REST API Endpoints | Endpoint | Method | Purpose | | -------- | ------ | ------- | | `/wp-json/fedistream/v1/artists/{id}/actor` | GET | ActivityPub actor profile | | `/wp-json/fedistream/v1/artists/{id}/inbox` | POST | ActivityPub inbox | | `/wp-json/fedistream/v1/artists/{id}/outbox` | GET | ActivityPub outbox | | `/wp-json/fedistream/v1/artists/{id}/followers` | GET | Followers collection | | `/wp-json/fedistream/v1/artists/{id}/following` | GET | Following collection | ### AJAX Actions | Action | Purpose | | ------ | ------- | | `fedistream_record_play` | Record track play | | `fedistream_toggle_favorite` | Add/remove favorite | | `fedistream_toggle_follow` | Follow/unfollow artist | | `fedistream_get_library` | Get user library | | `fedistream_get_followed_artists` | Get followed artists | | `fedistream_get_history` | Get listening history | | `fedistream_clear_history` | Clear listening history | | `fedistream_get_notifications` | Get user notifications | | `fedistream_mark_notification_read` | Mark notification read | | `fedistream_mark_all_notifications_read` | Mark all read | | `fedistream_delete_notification` | Delete notification | --- ## Session History ### 2026-01-28 - Initial Release v0.1.0 **Summary:** Consolidated all development phases (0.0.1 through 0.7.0) into initial release v0.1.0. **Completed:** - Implemented Phase 6 (WooCommerce Integration): - Custom product types for albums and tracks - Pricing models (Fixed, PWYW, NYP) - Digital delivery with secure downloads - Streaming access control based on purchases - Implemented Phase 7 (User Interactions): - User library with favorites, follows, history - Notification system (in-app and email) - Library shortcode and frontend page - Consolidated documentation: - Moved implementation details from PLAN.md to CLAUDE.md - Deleted PLAN.md (no longer needed) - Merged all changelog entries into single v0.1.0 release - Updated README.md with current features - Git operations: - Created initial commit on dev branch - Merged to main branch - Tagged as v0.1.0 - Push pending (requires credentials) **Files Created:** - `includes/WooCommerce/Integration.php` - `includes/WooCommerce/AlbumProduct.php` - `includes/WooCommerce/TrackProduct.php` - `includes/WooCommerce/DigitalDelivery.php` - `includes/WooCommerce/StreamingAccess.php` - `includes/User/Library.php` - `includes/User/LibraryPage.php` - `includes/User/Notifications.php` - `assets/js/library.js` - `assets/js/notifications.js` **Files Deleted:** - `PLAN.md` **Notes:** - Successfully pushed dev, main branches and v0.1.0 tag to origin - Remote URL updated from HTTPS to SSH for authentication - First release is now live at the repository ### 2026-01-28 - Bugfix v0.1.1 and Feature v0.2.0 **Summary:** Fixed WooCommerce integration timing bug, added plugin action links and user guide. **v0.1.1 - Bugfix:** - Fixed WooCommerce product types not appearing in product selector - Root cause: `Integration` constructor hooked `check_woocommerce` to `plugins_loaded` priority 5, but class was instantiated at priority 10 (too late) - Solution: Call `check_woocommerce()` directly in constructor **v0.2.0 - Features:** - Added Dashboard and Settings links to WordPress Plugins page - Created comprehensive `USERGUIDE.md` covering all features **Files Modified:** - `includes/WooCommerce/Integration.php` - Fixed hook timing - `includes/Plugin.php` - Added `add_plugin_action_links()` method **Files Created:** - `USERGUIDE.md` - Comprehensive user documentation **Notes:** - All releases pushed to origin (v0.1.1 and v0.2.0 tags) - Markdown linting fixes applied to USERGUIDE.md ### 2026-01-29 - License Management v0.3.0 **Summary:** Implemented license management integration and reorganized settings page into tabs. **Features:** - License management using `magdev/wc-licensed-product-client` package - Tabbed settings page: License, Default Settings, Integrations - License validation and activation via AJAX - License status banner with expiration display - Frontend license checks (unlicensed sites show message instead of content) - Admin/backend works regardless of license status **License Behavior:** - Backend (admin): Full access always - Frontend (player, shortcodes, ActivityPub): Requires valid license **Files Created:** - `includes/License/Manager.php` - License management wrapper class **Files Modified:** - `composer.json` - Added VCS repository and `magdev/wc-licensed-product-client` dependency - `includes/Plugin.php` - Tabbed settings page, license manager initialization, conditional frontend loading - `includes/Installer.php` - Added default license options - `includes/Frontend/Shortcodes.php` - Added unlicensed mode support - `includes/Frontend/Ajax.php` - Added license checks to public AJAX endpoints - `assets/js/admin.js` - License validation AJAX handlers - `assets/css/admin.css` - Tab and license status styling - `wp-fedistream.php` - Version bump to 0.3.0 - `CHANGELOG.md` - Added v0.3.0 entry **Notes:** - Package name is `magdev/wc-licensed-product-client` (not `wc-license-product-client`) - Uses Symfony HTTP Client via the license client package - License validation cached for 24 hours using WordPress transients ### 2026-01-29 - CI/CD Pipeline v0.4.0 **Summary:** Added Gitea Actions workflow for automated release package creation with multiple iterations to fix CI issues. **Features:** - Automated release builds triggered by `v*` tags - PHP 8.3 environment with required extensions - Production Composer dependency installation - Automatic translation compilation (.po to .mo) - Version verification (plugin version must match tag) - Proper WordPress plugin zip structure - SHA256 checksum generation - Package structure verification - Changelog extraction for release notes - Automatic Gitea release creation via API - Pre-release detection for tags containing `-` **Files Created:** - `.gitea/workflows/release.yml` - CI/CD release pipeline - `.gitmodules` - Git submodule configuration - `lib/wc-licensed-product-client/` - Submodule for private dependency **Files Modified:** - `CLAUDE.md` - Added CI/CD documentation and updated directory structure - `CHANGELOG.md` - Added v0.4.0 entry - `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:** - Requires `SRC_GITEA_TOKEN` secret configured in repository settings - Uses `shivammathur/setup-php@v2` for PHP setup - Uses Gitea API directly for release creation (not GitHub Actions) - Submodule uses relative URL for CI compatibility - Composer symlinks from `lib/wc-licensed-product-client` to vendor ### 2026-02-02 - Memory Leak Investigation v0.4.1 through v0.4.9 **Summary:** Investigated memory exhaustion issue when WP FediStream is used with WP Prometheus plugin. Multiple fix attempts were made but the root cause remains unresolved. **Problem:** - PHP Fatal error: Allowed memory size exhausted (1GB) - Error locations varied: Twig StagingExtension.php, Environment.php, ExtensionSet.php, WordPress class-wp-hook.php - Only occurs when WP Prometheus plugin is also active - Suspected infinite recursion through WordPress hook system **Fix Attempts (v0.4.1 - v0.4.8):** 1. **v0.4.1** - Added recursion depth tracking in `get_post_data()`, skip `the_content` filter at depth > 1 2. **v0.4.2** - Added `$in_shortcode_context` flag, all shortcode render methods enter context before data loading 3. **v0.4.3** - Changed boolean to counter (`$shortcode_context_depth`), added context to `template-wrapper.php` 4. **v0.4.4** - Fixed `get_the_excerpt()` which internally triggers `the_content` filter 5. **v0.4.5** - Added render depth tracking in `Plugin::render()`, strip shortcodes from content 6. **v0.4.6** - Added `$loading_page_template` flag to block shortcode rendering during page template loading 7. **v0.4.7** - Added `$rendering_main_template` hard lock in `Plugin::render()`, reduced MAX_RENDER_DEPTH to 2 8. **v0.4.8** - Nuclear option: ALWAYS skip `the_content` filter (didn't work, reverted) **v0.4.9 - Current State:** - Reverted nuclear option - Kept all other protections in place - Issue documented in README.md as known incompatibility **Files Modified:** - `includes/Frontend/TemplateLoader.php` - Multiple recursion protection mechanisms - `includes/Frontend/Shortcodes.php` - Shortcode context entry, page template loading check - `includes/Frontend/template-wrapper.php` - Page template loading flag, main template render flag - `includes/Plugin.php` - Render depth tracking, main template rendering lock - `README.md` - Added Known Issues section **Protection Mechanisms in Place:** 1. `$recursion_depth` counter in `get_post_data()` (max 3) 2. `$shortcode_context_depth` counter for nested shortcodes 3. `$loading_page_template` flag blocks shortcode rendering during page load 4. `$rendering_main_template` flag in `Plugin::render()` blocks parallel renders 5. `MAX_RENDER_DEPTH = 2` in `Plugin::render()` 6. Skip `the_content` and `get_the_excerpt()` when in protected context 7. Strip shortcodes from content when skipping content filter **Key Learnings:** - `get_the_excerpt()` internally calls `apply_filters('the_content', ...)` when generating auto-excerpts - `the_content` filter triggers `do_shortcode()` which can cause recursive shortcode processing - WordPress hook system (class-wp-hook.php) can itself be the recursion point - The interaction between FediStream and WP Prometheus appears to be at a fundamental WordPress level **Status:** RESOLVED - Issue was fixed in WP Prometheus plugin, removed from known incompatibilities ### 2026-02-02 - Prometheus Metrics & License Bypass v0.5.0/v0.5.1 **Summary:** Added Prometheus metrics integration and localhost license bypass for development. **v0.5.0 - Prometheus Metrics Integration:** - Created `includes/Prometheus/Integration.php` with comprehensive metrics collection - Content metrics: `fedistream_content_total`, `fedistream_genres_total`, `fedistream_moods_total` - Engagement metrics: `fedistream_plays_total`, `fedistream_plays_today`, `fedistream_favorites_total`, `fedistream_local_follows_total`, `fedistream_listening_history_entries` - User metrics: `fedistream_users_with_library`, `fedistream_users_following_artists`, `fedistream_notifications_total`, `fedistream_notifications_pending` - WooCommerce metrics (conditional): `fedistream_purchases_total`, `fedistream_customers_total`, `fedistream_products_total` - ActivityPub metrics (conditional): `fedistream_activitypub_followers_total`, `fedistream_activitypub_followers_by_artist`, `fedistream_activitypub_reactions_total` - New setting in Integrations tab to enable/disable - Uses `wp_prometheus_collect_metrics` action hook **v0.5.1 - Localhost License Bypass:** - Added `is_localhost()` method to License Manager - Bypasses license check on: `localhost`, `127.0.0.1`, `::1` - Also bypasses for TLDs: `.local`, `.test`, `.localhost`, `.dev.local` - Allows full plugin functionality during local development **Files Created:** - `includes/Prometheus/Integration.php` - Metrics collection class - `includes/Prometheus/index.php` - Security file **Files Modified:** - `includes/Plugin.php` - Added Prometheus integration, settings toggle, `is_prometheus_active()` method - `includes/Installer.php` - Added `wp_fedistream_enable_prometheus` default option - `includes/License/Manager.php` - Added `is_localhost()` method, bypass in `is_license_valid()` - `README.md` - Removed known incompatibility section, updated version badge **Integration Pattern:** Follows same pattern as WooCommerce/ActivityPub integrations: ```php if (get_option('wp_fedistream_enable_prometheus', 0) && $this->is_prometheus_active()) { new PrometheusIntegration(); } ``` **Notes:** - WP Prometheus memory leak issue was fixed in WP Prometheus itself - Metrics use simple COUNT queries for performance - ActivityPub followers limited to top 10 to avoid cardinality explosion - Default disabled (opt-in via Settings > Integrations) ### 2026-02-02 - Native PHP Templates v0.6.0 **Summary:** Removed Twig dependency and converted all templates to native PHP for improved performance and reduced dependencies. **Changes:** - Removed `twig/twig` from composer.json dependencies - Converted all 25 Twig templates to native PHP templates - Updated `Plugin.php` with new `render()` method using PHP include with output buffering - Added `render_partial()` helper method for including partials from within templates - Added `get_template_path()` method supporting theme overrides via `fedistream/` directory - Reduced plugin package size by removing Twig and its dependencies **Files Modified:** - `includes/Plugin.php` - Removed Twig initialization, new native PHP render methods - `includes/Frontend/template-wrapper.php` - Updated docblock - `composer.json` - Removed `twig/twig` dependency **Templates Created (25 PHP files replacing Twig):** - `templates/partials/` - card-artist.php, card-album.php, card-track.php, card-playlist.php - `templates/single/` - artist.php, album.php, track.php, playlist.php - `templates/archive/` - artist.php, album.php, track.php, playlist.php, taxonomy.php - `templates/shortcodes/` - artist.php, album.php, track.php, playlist.php, player.php, releases-grid.php, tracks-list.php, artists-grid.php - `templates/widgets/` - recent-releases.php, popular-tracks.php, featured-artist.php, now-playing.php **Templates Deleted (25 Twig files):** - All `.twig` files in templates directory **Benefits:** - No external template engine dependency - Improved performance (no Twig compilation) - Native WordPress template overriding support - Smaller plugin package size - Simpler debugging with standard PHP