diff --git a/dist/waveform-player.cjs b/dist/waveform-player.cjs index 8739cf3..ae32f4e 100644 --- a/dist/waveform-player.cjs +++ b/dist/waveform-player.cjs @@ -1056,11 +1056,7 @@ var WaveformPlayer = class _WaveformPlayer { this.titleEl = this.container.querySelector(".waveform-title"); this.artistEl = this.container.querySelector(".waveform-artist"); this.artworkEl = this.container.querySelector(".waveform-artwork"); - if (this.artworkEl) { - this.artworkEl.addEventListener("error", () => { - if (!this.artworkEl.src.startsWith("data:")) this.artworkEl.src = ARTWORK_FALLBACK; - }); - } + this.bindArtworkFallback(this.artworkEl); this.currentTimeEl = this.container.querySelector(".time-current"); this.totalTimeEl = this.container.querySelector(".time-total"); this.bpmEl = this.container.querySelector(".waveform-bpm"); @@ -1073,6 +1069,94 @@ var WaveformPlayer = class _WaveformPlayer { this.resizeCanvas(); this.updateBPMDisplay(); } + /** + * Bind the artwork error fallback to an image element. + * + * @param {HTMLImageElement|null} img - Artwork image element. + * @private + */ + bindArtworkFallback(img) { + if (!img) return; + img.addEventListener("error", () => { + if (!img.src.startsWith("data:")) img.src = ARTWORK_FALLBACK; + }, { signal: this._ac.signal }); + } + /** + * Create an artwork image element matching the initial player markup. + * + * @returns {HTMLImageElement} Artwork image element. + * @private + */ + createArtworkElement() { + const img = document.createElement("img"); + img.className = "waveform-artwork"; + img.style.width = "40px"; + img.style.height = "40px"; + img.style.borderRadius = "4px"; + img.style.objectFit = "cover"; + img.style.flexShrink = "0"; + this.bindArtworkFallback(img); + return img; + } + /** + * Create an artist text element matching the initial player markup. + * + * @returns {HTMLSpanElement} Artist text element. + * @private + */ + createArtistElement() { + const span = document.createElement("span"); + span.className = "waveform-artist"; + return span; + } + /** + * Reconcile artist metadata and markup for the current track. + * + * @param {string|null} artist - Artist text, or a falsy value to remove it. + * @private + */ + syncArtist(artist) { + this.options.artist = artist || null; + if (!this.options.showInfo) return; + if (!artist) { + this.artistEl?.remove(); + this.artistEl = null; + return; + } + if (!this.artistEl) { + const titleEl = this.container.querySelector(".waveform-title"); + if (!titleEl) return; + this.artistEl = this.createArtistElement(); + titleEl.after(this.artistEl); + } + this.artistEl.textContent = artist; + this.artistEl.style.display = ""; + } + /** + * Reconcile artwork metadata and markup for the current track. + * + * @param {string|null} artwork - Artwork image URL, or a falsy value to remove artwork. + * @param {string} artworkAlt - Artwork alt text. + * @private + */ + syncArtwork(artwork, artworkAlt = "") { + this.options.artwork = artwork || null; + this.options.artworkAlt = artworkAlt || ""; + if (!this.options.showInfo) return; + if (!artwork) { + this.artworkEl?.remove(); + this.artworkEl = null; + return; + } + if (!this.artworkEl) { + const textEl = this.container.querySelector(".waveform-text"); + if (!textEl) return; + this.artworkEl = this.createArtworkElement(); + textEl.before(this.artworkEl); + } + this.artworkEl.src = artwork; + this.artworkEl.alt = artworkAlt || ""; + } /** * Create audio element * @private @@ -1622,10 +1706,12 @@ var WaveformPlayer = class _WaveformPlayer { * @param {string|null} [artist=null] - Track artist; pass `''` to hide * the artist row, or null to keep the existing one. * @param {Object} [options={}] - Additional options to merge (e.g. - * `preload`, `artwork`, `markers`, `autoplay`). + * `preload`, `artwork`, `artworkAlt`, `markers`, `autoplay`). * @returns {Promise} */ async loadTrack(url, title = null, artist = null, options = {}) { + const hasArtworkOption = Object.prototype.hasOwnProperty.call(options, "artwork"); + const hasArtworkAltOption = Object.prototype.hasOwnProperty.call(options, "artworkAlt"); if (this.isPlaying) { this.pause(); } @@ -1647,23 +1733,29 @@ var WaveformPlayer = class _WaveformPlayer { this.waveformData = []; this.options = mergeOptions(this.options, { url, - title: title || this.options.title, - artist: artist || this.options.artist, + title: title === null ? this.options.title : title, + artist: artist === null ? this.options.artist : artist, ...options }); + if (hasArtworkOption) { + this.options.artwork = options.artwork || null; + } + if (hasArtworkAltOption) { + this.options.artworkAlt = options.artworkAlt || ""; + } else if (hasArtworkOption) { + this.options.artworkAlt = this.options.artwork ? DEFAULT_OPTIONS.artworkAlt : ""; + } if (options.preload && this.audio) { this.audio.preload = options.preload; } - if (this.artistEl) { - if (artist) { - this.artistEl.textContent = artist; - this.artistEl.style.display = ""; - } else if (artist === "") { - this.artistEl.style.display = "none"; - } + if (artist !== null) { + this.syncArtist(artist); } - if (options.artwork && this.artworkEl) { - this.artworkEl.src = options.artwork; + if (hasArtworkOption || hasArtworkAltOption) { + this.syncArtwork( + hasArtworkOption ? options.artwork : this.options.artwork, + hasArtworkAltOption ? options.artworkAlt : this.options.artworkAlt + ); } this.options.markers = options.markers || []; this.options.waveform = options.waveform || null; diff --git a/dist/waveform-player.cjs.map b/dist/waveform-player.cjs.map index d8bc9fd..8423696 100644 --- a/dist/waveform-player.cjs.map +++ b/dist/waveform-player.cjs.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/js/index.js", "../src/js/utils.js", "../src/js/drawing.js", "../src/js/bpm.js", "../src/js/audio.js", "../src/js/themes.js", "../src/js/core.js"], - "sourcesContent": ["/**\n * @module index\n * @description Public entry point for the WaveformPlayer library.\n *\n * Wires together the runtime surfaces for the player: it re-exports the\n * {@link WaveformPlayer} class (default and named), exposes a static\n * `WaveformPlayer.init` hook, scans the DOM for declarative `[data-waveform-player]`\n * markup and auto-instantiates a player for each match, and attaches the class\n * to `window` for plain `