WP Manifestindependent plugin directory
manifest / media / sleek-audio-player

Sleek Audio Player

A sleek, modern audio player for WordPress - perfect for musicians, podcasters, and bands.

by Martin Gräbing · github.com/duesseldorp/sleek-audio-player · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/duesseldorp/sleek-audio-player/archive/refs/heads/main.zip

A modern, fast and shareable audio player for WordPress.
Built for music, podcasts and playlists – with strong UX, clean SEO and reliable performance.

👉 Product page & background: https://www.duesseldorp.de/sleek-audio-player/

Current Version: 2.15.0


Why Sleek Audio Player?

Most WordPress audio players are either too basic or overloaded with features that hurt performance and usability.
I built Sleek Audio Player because I needed a solution that:

  • feels modern and intuitive
  • loads fast and works reliably on all devices
  • makes sharing individual tracks actually useful
  • treats audio as first class content, not as an afterthought

This plugin focuses on clarity, performance and distribution instead of visual noise.


Core Features

  • Modern UI
    Clean layout with optional waveform and visualizer for intuitive navigation.

  • Playlist & Track Support
    Use single tracks or playlists without additional configuration.

  • Streaming Service Integration
    Add links to Spotify, Apple Music, Amazon Music and SoundCloud for each track. Links appear in the player's "More" menu.

  • Shareable Audio
    Track specific Open Graph and Twitter Card support so shared links show the correct title, cover and metadata.

  • Autoplay Support
    Optional autoplay parameter for shortcodes to start playback automatically on page load.

  • SEO for Audio Content
    Structured data (JSON-LD) for MusicRecording and MusicPlaylist to help search engines understand your content.

  • Performance First
    Lightweight frontend, no unnecessary dependencies, optimized loading behavior.

  • Mobile Friendly
    Touch gestures, responsive layout and smooth playback on mobile devices.


Typical Use Cases

  • Musicians publishing tracks or albums
  • Podcasters embedding episodes and playlists
  • Bands or labels sharing audio previews
  • Blogs that want audio content to be discoverable and shareable

Architecture & Tech

  • WordPress Plugin
  • PHP for backend logic
  • JavaScript for playback and UI interactions
  • HTML & CSS for a clean, minimal interface
  • JSON-LD structured data based on Schema.org

Every technical decision aims to keep the plugin:

  • understandable
  • maintainable
  • performant

Installation

  1. Download or clone this repository
  2. Upload the plugin folder to /wp-content/plugins/
  3. Activate Sleek Audio Player in the WordPress admin panel
  4. Insert the player via block or shortcode

Usage

Basic Shortcode

[sleek_player id="123"]

Wide Layout

[sleek_player id="123" layout="wide"]

With Autoplay

[sleek_player id="123" autoplay="true"]

Combined Parameters

[sleek_player id="123" layout="wide" autoplay="true"]

Adding Streaming Links

In the WordPress backend, edit your playlist and add streaming service URLs for each track:

  • 🎵 Spotify
  • 🍎 Apple Music
  • 📦 Amazon Music
  • ☁️ SoundCloud

These links will appear in the player's "More" menu for each track.

Detailed usage examples are documented on the product page.


Requirements

  • WordPress 5.0+ and PHP 7.4+
  • Serve your site and its audio files over HTTPS. The audio visualizer uses the Web Audio API, which only works when the audio is served from the same origin as the page (or from a trusted CDN) over the same protocol. If your page loads over HTTPS but an audio file is referenced over HTTP (mixed content), the browser blocks it and the visualizer stays disabled – playback may still work, but no bars/waveform animation appears. Make sure WordPress Site/Home URL and all stored media URLs use https://.

Security & Privacy

  • No external tracking
  • No third party APIs required
  • Audio files stay on your server or your configured storage
  • No user data collected by the plugin itself

Security issues can be reported privately. See SECURITY.md.


Roadmap

  • [ ] Extended playlist customization
  • [ ] More player layout variants
  • [ ] Improved accessibility options
  • [ ] Optional advanced analytics hooks

The roadmap is driven by real usage, not feature checklists.


Context & Documentation

I document the design decisions and use cases behind this plugin openly on my site:
👉 https://www.duesseldorp.de/sleek-audio-player/


Accessibility

What is checked, how, and what is not — so the claim can be judged rather than believed.

Verified automatically on every push

Guarantee How
Both sliders can be focused and operated by keyboard End-to-end tests press arrow keys, Home and End and read the resulting value
The keyboard never triggers two handlers for one press A focused volume slider must land on 65 %, not 60 %
Sliders report their real value to assistive technology aria-valuenow must leave 0 during playback; aria-valuetext must contain a time
The track title is announced when it changes aria-live="polite" asserted on the element
Every control has a non-empty accessible name All eleven controls checked for aria-label and title
prefers-reduced-motion stops the continuous animations Tested with the setting emulated, including that an explicit visualizer choice still wins
The default theme meets WCAG 2.1 AA contrast tools/check-contrast.py computes the ratios from the stylesheet

Measured contrast of the shipped theme

Element Ratio AA (4.5:1)
Track title 17.59:1
Track title in playlist 14.97:1
Button icons and subtitle 8.03:1
Artist and time display 4.85:1 ✅ (was 3.91:1 — fixed in 2.10.1)
Active toggles, progress, visualizer 5.08:1
Track numbers 1.96:1 open

Known gaps

  • Track numbers fail contrast at 1.96:1. Raising them is a visible design change to a colour shared with other elements, so it is a decision about the look rather than a bug fix, and it is still open.
  • Custom themes are not checked. The Theme Manager lets anyone choose their own colours, including unreadable combinations. Only the shipped default is verified.
  • No real screen reader has been used. Everything above tests the markup and the values in the page — that the roles, names and live regions are correct and current. Whether NVDA, JAWS or VoiceOver announce them usefully is a different question, and it has not been answered. Do not read "accessible" as "tested with assistive technology".
  • One browser. The tests run in Chromium. Firefox and Safari are untested.
  • Mobile screen readers (TalkBack, VoiceOver iOS) are untested.

This is deliberately not a claim of WCAG 2.1 AA conformance. It is a list of what is checked and what is not.

Changelog

Version 2.15.0 (2026-09-14)

2.14.0 brought the preview image back, but a shared song showed the page instead of the song. With an SEO plugin active the player stepped aside entirely, so sharing "Die Stadt steht Kopf!" out of the carnival playlist showed the carnival page's title and picture.

All in One SEO knows the page, not the songs inside a player: to it, ?playlist=10548&track=2 is an unknown query string. The player now tells it which song was shared, through AIOSEO's own aioseo_facebook_tags and aioseo_twitter_tags filters:

  • Title, description, og:url and image are swapped inside the one set of tags AIOSEO prints. og:url too, because Facebook follows it and would otherwise show the page again
  • Without a track in the link, AIOSEO's preview stays untouched
  • The image is the largest size of the cover that stays under 300 KB, the limit WhatsApp is commonly reported to apply. On the production site that is the 300 px version at 116 to 143 KB rather than the 1024 px PNG of 1.3 to 2 MB. The player's own tags on sites without an SEO plugin use the same choice now
  • Yoast SEO, Rank Math, SEOPress and The SEO Framework keep the 2.14.0 behaviour for now: one set of tags, the page's preview
  • The preview text of a shared song is translatable; it used to say "by" on German sites too

Covered by end-to-end tests with a stand-in that prints its tags the way All in One SEO's source does.

Version 2.14.0 (2026-09-14)

Shared links showed no preview image on a site with an SEO plugin. The player prints Open Graph tags for a shared track, and so does every SEO plugin. The page then offers WhatsApp, Facebook and the rest two og:image candidates. Measured on the production site with All in One SEO: the SEO plugin offered the 240 KB JPEG made for sharing, the player the track cover, a 1024 px PNG of 1.3 to 2 MB.

  • With Yoast SEO, All in One SEO, Rank Math, SEOPress or The SEO Framework active, the player prints no Open Graph or Twitter tags of its own. The preview belongs to the SEO plugin
  • Without an SEO plugin nothing changes
  • The trade-off: sharing one song out of a playlist with several now shows the page's preview rather than that song's title and cover
  • If your SEO plugin's social output is switched off, hand the job back: add_filter('sleekaudio_seo_plugin_prints_open_graph', '__return_false');

Links shared before the update can keep their old preview for a while, because the services store one per address. Facebook's Sharing Debugger fetches a page again on request.

Covered by an end-to-end test with a stand-in SEO plugin, active only on requests that ask for it.

Version 2.13.0 (2026-08-16)

The visualizers were spreading their bars evenly across 0–24 kHz. Hearing does not work that way: 100 Hz to 200 Hz is an octave, 10 000 Hz to 10 100 Hz is nothing. Measured with 64 bars at a 48 kHz sample rate:

Range before after
Bass, below 250 Hz 1 bar 22
250 Hz – 2 kHz (voice, melody) 5 21
2 – 8 kHz 16 14
Above 8 kHz, almost no energy 42 7

Two thirds of the display was showing frequencies music barely uses. Sampled on a real track beforehand, the top quarter of the canvas never moved at all and the rest fell away in a straight ramp — the classic look of a linear FFT.

Every bar now covers a musical interval instead. One change in one helper, used by all eleven visualizers, so they all improve together. The loudest bin within a band is taken rather than the average, because averaging washes a single strong tone out into its quiet neighbours.

Covered by an end-to-end test that exploits the fixtures being pure sine tones: 440 Hz belongs near the middle of the display, not at the left edge.

Version 2.12.0 (2026-08-16)

Every cover was loaded eagerly, including the ten nobody sees. Measured on the production playlist: eleven images at roughly 260 KB each, about 2.9 MB per page view, for the single cover that is on screen. It hit every visitor, including those who never pressed play.

  • The active cover keeps loading="eager" and gains fetchpriority="high"
  • The rest are loading="lazy"
  • Swiping stays instant because the reachable neighbours are fetched ahead of time — the same approach the player already uses to preload the next track's audio. Lazy loading alone would have made a swipe reveal a blank slide while the finger is still moving
  • Covers carry srcset/sizes where the source really is that attachment, so a phone can take a smaller size instead of the 1024 px original for a ~400 px slot

Found by measuring rather than reading: the audio metadata requests I had suspected turned out to cost almost nothing — the files carry a Xing header, so duration comes from the first few kilobytes. The covers were a thousand times larger, and invisible until counted.

Version 2.11.0 (2026-08-16)

The block editor was marked up for translation but could never be translated. Its labels sit in wp.i18n.__() calls with the correct text domain, so the code looked right — but tools/make-pot.py only read PHP, so a translator never saw the strings, and wp_set_script_translations() was missing, so even a finished translation would not have loaded. The same shape as the text-domain path bug: markup promising something the plumbing never delivered.

  • make-pot.py now reads block.js too — six further strings
  • tools/make-json.py builds the .json files WordPress loads for scripts, named with the md5 of the script path that load_script_textdomain() expects
  • wp_set_script_translations() connects the two
  • build-zip.py ships the .json — it was not on the whitelist, so the file would have existed in the repository, CI would have been green, and every installed copy would still have been English

Done the standard way rather than through wp_localize_script on purpose: if the plugin is ever listed on wordpress.org, translate.wordpress.org generates exactly these .json files automatically — but only for plugins that call wp_set_script_translations().

Translating the plugin is now documented. The important part was undocumented: WordPress reads wp-content/languages/plugins/ before the plugin's own folder, so anyone can translate the plugin for their site by dropping a .mo there. No pull request, no build step, and it survives updates. Right-to-left languages are named as unsupported rather than left to be discovered.

Version 2.10.1 (2026-08-15)

A contrast defect the audit found. The artist name and the time display sat at 3.91:1 against the player background — below the 4.5:1 that WCAG 2.1 AA requires for normal text. Not decoration: that is the artist's name. Raised to 4.85:1, a change barely visible to anyone who could read it before.

tools/check-contrast.py now computes these ratios from the stylesheet and CI fails when a shipped colour drops below its minimum, so "the default theme meets AA" is a checked fact rather than a sentence in a readme.

The same measurement found the track numbers at 1.96:1. Raising them changes a colour shared with other elements, so that is a decision about the look and remains open — and is listed as such in the new Accessibility section rather than quietly ignored.

Version 2.10.0 (2026-08-15)

prefers-reduced-motion is now respected. That setting is not a guess about the visitor — it is a switch they set themselves in their operating system, usually because screen motion makes them dizzy or nauseous.

The player distinguishes three kinds of motion instead of switching everything off:

Ken Burns (20 s, endless), vinyl rotation (8 s), vinyl shimmer, the visualizer stop
Entrances of the player, the track rows and the embed dialog keep their end state, skip the movement
Loading spinner, buffering indicator, skeleton shimmer keep moving

The third row is the reason this is a list and not the usual * { animation: none }: those animations are the information. Without them the player looks broken rather than calm, and they are small and local — not motion sweeping across the view.

The visualizer is a default, not a lock. With reduced motion it starts off, but cycling it on with V or a double click still wins, because an explicit choice should outrank a system default.

Version 2.9.0 (2026-08-15)

The sliders keep their promise now. Both carry role="slider", which tells assistive technology "this can be focused and adjusted". Neither had a tabindex, so neither could be focused at all.

  • Arrow keys move by 5 seconds or 5 %, Page Up/Down by 30 seconds, Home and End jump to the ends
  • Keyboard focus is visible again. The stylesheet removed the outline with !important so a mouse click would not leave a ring behind; that also left keyboard users with no idea where they were. :focus-visible matches keyboard focus only, so the ring is back for exactly the people who need it
  • The volume panel is visibility: hidden until hovered, which keeps it out of the tab order. It now also opens on :focus-within, so tabbing to the volume button reveals the slider and the next Tab reaches it

A bug found while doing it: the global keyboard shortcuts set audio.volume directly, past the player's own setVolume(). The sound got quieter, but the visible slider did not move, aria-valuenow stayed put, and the preference was never saved — undoing 2.7.1's work through the keyboard. The shortcut now asks the player through a sap:volume-step event, and Mute clicks the real button like every other shortcut already did.

Correction to an earlier claim: I previously said seeking and volume were keyboard-inoperable. That was wrong — the global shortcuts have always worked. What was missing is that the sliders themselves could not be reached or operated, which is what role="slider" promises and what a screen reader user is told to expect.

Version 2.8.0 (2026-08-15)

The waveform now shows the time under the cursor. Seeking used to be a guess: the bar gave no clue where a click would land, which matters most in exactly the case where seeking matters most - a long mix.

  • The label is clamped to the player's edges, so it stays readable at the very start and end of a track
  • It is measured only when its text changes rather than on every mouse move, so a fast pointer does not force a reflow per pixel
  • pointer-events: none keeps it out of the click path; seeking works exactly as before
  • Touch devices never get it: they have no hover, and a tap would leave the label behind. Guarded with matchMedia('(hover: hover) and (pointer: fine)')

Version 2.7.1 (2026-08-15)

Accessibility, first step — three defects rather than three enhancements:

  • Both sliders carry role="slider", which promises assistive technology a readable value. Nothing ever wrote aria-valuenow: the progress slider announced 0 for the entire track and the volume slider 70 regardless of the actual volume. Both now report their real value
  • The progress slider also exposes aria-valuetext with the time ("1:23 von 3:45"), which is far more useful than a percentage. It updates only on whole percent steps, so timeupdate firing four times a second does not turn into four announcements
  • The track title is now an aria-live="polite" region. A sighted user sees the title change; a screen reader user previously got nothing at all

Still open on the accessibility list: keyboard operation of the two sliders (they have no tabindex and cannot be focused) and prefers-reduced-motion for the 23 animation declarations.

Version 2.7.0 (2026-08-15)

The admin interface is now translatable too. 2.6.1 completed the visitor-facing player; this covers everything the site owner sees:

  • Settings page: CDN, Umami, URL protection, cover click, remember position, all twelve visualizer options and the full BunnyCDN guide including the CORS instructions
  • Playlist editor: field labels and placeholders, the embed shortcodes, the copy confirmations
  • The WordPress menu entries themselves ("Player Settings", "Settings") — they were hardcoded English and stayed English on a German installation
  • 74 further strings, all translated into German (151 in total now)

One subtlety worth naming: admin.js rebuilds a track row in JavaScript when you add one, duplicating the PHP markup. Translating only the PHP would have produced English placeholders in new rows next to German ones in existing rows. Both now read the same strings, via a sapAdminText() helper that mirrors the player's sapText() — English fallback mandatory.

Theme Manager and Waveform Analysis are covered too. Both classes contained not a single translation call — every colour label, button, badge, table header, confirmation dialog and AJAX message was hardcoded English (the waveform page even had one German sentence mixed in). That is 86 further strings; 237 in total, all translated.

A bug this uncovered: the waveform page selected pending files by reading the status column's text (.text().includes('Pending')). Translating that text would have left the analyse button unable to find anything — a feature broken by its own translation. The state now lives in a data-status attribute, where the display language cannot reach it.

Publish box layout fixed. "Entwurf speichern" and "Vorschau anzeigen" dropped onto separate lines. Measured on the running page: the two buttons need 293px, the sidebar column offers 292px — core WordPress floats them side by side, which only holds while the labels are as short as in English. The plugin made it worse by adding 12px padding around the box, costing another 24px. They now share the width in a flex row instead of overflowing.

Deliberately left in English: product names (Spotify, Apple Music, bunny.net, Umami), example URLs, shortcode snippets and the Access-Control-Allow-Origin header name — translating those would make the instructions wrong.

Version 2.6.1 (2026-08-15)

The translations from 2.6.0 never actually reached the site.

  • The plugin loaded its translation files from includes/languages/ instead of languages/ — a leftover from the 2.5.7 restructuring, when the loading code moved into includes/ and its __FILE__ started pointing one directory too deep. Nothing failed loudly: WordPress simply found no translations and showed English. Found by testing against a real German site
  • Fixed at the class of bug, not the instance: plugin paths are now resolved once in the main file (SLEEKAUDIO_PLUGIN_BASENAME), and CI rejects any __FILE__-relative plugin path inside includes/

The server-rendered markup was never translatable at all:

  • Every tooltip and screen-reader label (Previous track, Play or pause, Volume control, Playback progress, …), plus "Select a track", the track count and the Download entry, were hardcoded English. 2.6.0 had translated only the strings JavaScript writes at runtime
  • The initial menu labels now use the same complete sentences as the JavaScript, so a label no longer jumps from English to German on first click

Plural forms were silently dropped:

  • tools/po2mo.py did not understand msgid_plural, so the track count could never be translated. It now compiles plural entries the way WordPress reads them

New safeguards:

  • tools/make-pot.py generates the translation template from the sources; CI fails if a translatable string is missing from it or has no German translation
  • Two new end-to-end tests: every control has a non-empty accessible name, and the track count uses the plural form

Version 2.6.0 (2026-08-14)

The player interface is now fully translatable:

  • Around 30 strings that the player wrote into the page at runtime were hardcoded English, so German visitors saw "Repeat: Off", "Speed: 1.25x" or "Network error" even on a German site. They now go through the translation system and are translated into German
  • Covered: the More menu labels (Repeat, Speed, Sleep Timer, Cover, Adaptive Colors), all 11 visualizer names, the "Copied!" confirmation and every playback error message
  • Menu labels are stored as complete sentences rather than assembled from fragments, so translations are free to use their own word order
  • A missing or misspelled key falls back to the English original by design — it can never render an empty label or "undefined"

Version 2.5.8 (2026-08-14)

Attribution (no functional change):

  • Every shipped file now carries a copyright and licence notice. The includes/ classes had none, and the minified assets carried none either — minifiers strip all comments, so the very files visitors load were unattributed. tools/minify.py now prepends a banner with version, holder, licence and repository URL
  • The main plugin file gained the full GPL notice (holder, redistribution terms, warranty disclaimer) beyond the plugin-header lines

Version 2.5.7 (2026-08-14)

Internal restructuring (nothing changes for users):

  • The three PHP classes moved out of the main file into includes/; sleek-audio-player.php is now a 139-line bootstrap instead of 4,153 lines. Same options, same shortcodes, same markup, same behaviour
  • Not a line of logic was changed: the move was scripted and verified by comparing the function/class inventory before and after (79 symbols, unchanged) plus the full test suite
  • The distribution package and CI now verify that every required PHP file actually ships

Version 2.5.6 (2026-08-14)

Improvements:

  • The More menu no longer closes on the slightest scroll. It stays open within a small tolerance (8px) and is repositioned to stay attached to its button; only a deliberate scroll closes it. Momentum scrolling on touch devices emits tiny scroll events right after the tap that opens the menu, which previously made it flash open and shut

Version 2.5.5 (2026-08-13)

Translations (German now complete for the visitor-facing UI):

  • Several player strings were already wrapped for translation in the code but were missing from the translation catalog, so German visitors saw English: the "More" menu (Share, Shuffle, Sleep Timer and its presets, Cover, Adaptive Colors, Embed Player) and the embed dialog (Layout, Embed Code, Copy Code, Preview, Close). These are now translated
  • Added the missing post-type labels (Add New, All Playlists) and the No playable tracks found. notice
  • Rebuilt sleek-audio-player.pot and sleek-audio-player-de_DE.po to reflect the code exactly: removed ~35 catalog entries that were never actually wrapped in translation functions (hardcoded admin-area strings) plus a duplicate Tap to Play, and recompiled the .mo
  • No code behavior change beyond the now-correct translations; the admin settings/theme/waveform pages remain English (their strings are not translatable yet)

Version 2.5.4 (2026-08-13)

Bug Fixes:

  • Fixed playback stopping between tracks on locked screens / backgrounded tabs (reported on Android Chrome after ~2 songs): play() is now called immediately in the same task chain as the ended event instead of being deferred via loadeddata/timeouts - deferred timers are throttled on hidden pages and the delayed call lost its autoplay allowance. The event/timeout machinery remains as a fallback for legacy mobile quirks
  • Blocked playback now also resumes on the first touch anywhere on the page and on bfcache restores (pageshow), not just via the overlay button or a visibility change

Version 2.5.3 (2026-08-10)

Plugin Check pass (no functional changes for visitors):

  • Theme queries now use the WordPress object cache (wp_cache_*) with invalidation on every write - faster on sites with persistent object caching, and resolves all NoCaching warnings properly instead of suppressing them
  • Waveform peak data from the editor is now element-wise cast to floats before saving (defense in depth)
  • Every remaining Plugin Check warning carries an inline justification at the exact line (public GET endpoints without nonces, custom-table queries, gated error_log, binary streaming via fread, load_plugin_textdomain for non-wordpress.org installs)
  • uninstall.php: prefixed the global variable, moved the phpcs justification to the correct line

Version 2.5.2 (2026-08-09)

WordPress.org compliance (internal, no functional changes):

  • Renamed all global PHP symbols to unique prefixes (sleekaudio_* functions, SleekAudio_* classes, SLEEKAUDIO_* constants) per plugin directory guidelines; stored data (options, post type, meta keys) is unchanged, so updating is seamless
  • Hardened SQL: table-existence check now uses $wpdb->prepare(); remaining table-name interpolations documented (safe: names derive from $wpdb->prefix)
  • Added the required "External Services" disclosure section (Umami Analytics, CDN URL rewriting - both opt-in) to readme.txt
  • New tools/build-zip.py builds a clean distribution ZIP without development files

Version 2.5.1 (2026-08-09)

Documentation:

  • Documented that the audio visualizer requires audio to be served over the same origin/protocol as the page (or a trusted CDN). On an HTTPS page, audio referenced over HTTP is blocked as mixed content and the visualizer stays disabled – ensure Site/Home URL and stored media URLs use https://

Version 2.5.0 (2026-08-09)

Performance:

  • Player assets (JS/CSS) now load only on pages that actually contain a player - previously ~200 KB were loaded on every page of the site. Detection covers shortcodes, the Gutenberg block, playlist pages, and page-builder/widget rendering (late enqueue with inline stylesheet printing to avoid unstyled flashes)
  • Minified builds are now shipped and served by default (player.js 141 KB → 72 KB, player.css 48 KB → 34 KB); set SCRIPT_DEBUG to serve the readable sources. Build via python tools/minify.py

Bug Fixes:

  • Fixed player silently stopping between tracks when the browser blocks the automatic transition (e.g. locked screen or backgrounded tab on Android Chrome) - the "Tap to Play" overlay now always appears and playback resumes automatically once the page is visible again
  • Fixed More menu rendering with excessive spacing on pages where the page builder runs wpautop over the shortcode output (e.g. homepage) - the player markup is now emitted without inter-tag whitespace so wpautop cannot inject stray <br>/<p> elements, plus a CSS safety net hides any that still appear
  • Fixed track durations being deleted when saving a playlist - the duration field is now persisted in the editor form
  • Added missing SoundCloud field to newly added track rows in the playlist editor
  • Added SoundCloud links to JSON-LD schema markup (sameAs), as documented since 2.2.0

Maintenance:

  • Fixed translations never loading: load_plugin_textdomain was missing and no compiled .mo file was shipped - the German translation now actually works
  • "Tap to Play" overlay text is now translatable
  • Uninstall now also removes the Umami script URL / website ID options and stored waveform data
  • Corrected license statement in readme.md (GPL v2 or later, matching the plugin header)

Version 2.4.5 (2026-08-07)

Bug Fixes:

  • Fixed More menu appearing in the wrong position when the player is placed inside transformed/animated theme sections (e.g. page builder homepages) - position: fixed coordinates are now converted to the correct containing block
  • Hardened More menu item CSS against theme style overrides that caused offset entries with excessive spacing

Version 2.3.2 (2026-03-24)

Bug Fixes:

  • Fixed "Tap to Play" overlay not disappearing when starting playback from playlist
  • Overlay now automatically removes when audio starts playing via any method (play button, track selection, or overlay click)

Version 2.3.1 (2026-03-24)

Bug Fixes:

  • Fixed autoplay not working when autoplay="true" parameter is set in shortcode
  • Added autoplay logic for normal page loads (not just shared links)
  • Autoplay now triggers after audio metadata loads for better reliability

Version 2.3.0 (2026-03-24)

New Features:

  • Added autoplay parameter to shortcode: [sleek_player id="123" autoplay="true"]
  • Enables automatic playback on page load (subject to browser autoplay policies)
  • Shows "Tap to Play" overlay if browser blocks autoplay
  • Works with all shortcode parameters (layout, etc.)

Version 2.2.2 (2026-03-24)

UI Improvements:

  • Fixed track list alignment issues in backend
  • All cover images now perfectly aligned at same height
  • Improved grid layout with consistent spacing
  • Elements in first row are vertically centered
  • Streaming links properly aligned in second row

Version 2.2.1 (2026-03-24)

UI Improvements:

  • Optimized backend track list layout for better visual consistency
  • Streaming link input fields now display in a clean second row
  • All four streaming services (Spotify, Apple Music, Amazon, SoundCloud) appear side-by-side
  • Improved responsive behavior for tablet and mobile screens

Version 2.2.0 (2026-03-24)

New Features:

  • Added SoundCloud integration alongside existing streaming services
  • Backend: SoundCloud link field in track list for each track
  • Frontend: SoundCloud link appears in player's "More" menu
  • CSS: Orange hover effect matching SoundCloud brand colors
  • Schema markup extended to include SoundCloud links

Technical Changes:

  • Removed global streaming links in favor of track-specific links only
  • Cleaned up meta data storage for better performance
  • Updated grid layout to accommodate four streaming services

Version 2.1.4 (2026-03-18)

Bug Fixes:

  • Improved mobile autoplay reliability with loadeddata event and timeout fallback
  • Fixed race condition in track loading on mobile browsers
  • Added 500ms fallback timeout to ensure playback starts even if events don't fire
  • Switched from canplay to loadeddata event for better mobile compatibility
  • Prevents duplicate play attempts with flag-based control

Technical Details:

  • The canplay event was unreliable on mobile browsers during track transitions
  • Now uses loadeddata event which fires when the first frame is loaded (more reliable)
  • Timeout fallback ensures playback continues even if the event system fails
  • This combination provides robust autoplay on all mobile devices

Version 2.1.3 (2026-03-18)

Bug Fixes:

  • Fixed mobile playback issue where player would stop after one song
  • Improved audio readiness detection on mobile browsers
  • Enhanced promise handling for autoplay after track ends
  • Added canplay event listener to ensure audio is ready before playback
  • Better error logging for mobile playback debugging

Technical Details:

  • Mobile browsers have strict autoplay policies that can block automatic track progression
  • The fix ensures the audio element is fully ready (readyState >= 3) before attempting playback
  • Improved handling of the ended event to reliably trigger the next track on mobile devices

Version 2.1.2

  • Previous stable release

License

Copyright © 2025–2026 Martin Gräbing

Released under the GNU General Public License v2 or later — see LICENSE.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.