UNPKG

@sanyueqi/web-components

Version:

Web components

577 lines (498 loc) 21 kB
(function (factory) { typeof define === 'function' && define.amd ? define(factory) : factory(); })((function () { 'use strict'; /** * 音乐播放器插件 * 基于 APlayer 和 MetingJS 的音乐播放器 */ class MusicPlayerPlugin { playId = 'aplayer-container' constructor() { this.loadedAPlayer = false; this.loadedMetingJS = false; this.hasMusicInteracted = false; } /** * 初始化音乐播放器 * @param {Object} config - 播放器配置 */ init(config) { if (!config.enable) return; if (config.playId) { this.playId = config.playId; } // 确保在浏览器环境中运行 if (typeof window === 'undefined') return; this.config = config; // 预处理配置 this.processConfig(); // 加载 CSS this.loadCSS(); // 初始化播放器 this.initPlayer(); } /** * 预处理配置 */ processConfig() { // 处理 APlayer CSS 路径 - 修改这里 this.aplayerCssPath = this.config.aplayerCss ? (this.config.aplayerCss.startsWith("http://") || this.config.aplayerCss.startsWith("https://")) ? this.config.aplayerCss : this.processUrl(this.config.aplayerCss) : "https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.css"; // 默认 CDN // 处理 APlayer JS 路径 - 修改这里 this.aplayerJsPath = this.config.aplayerJs ? (this.config.aplayerJs.startsWith("http://") || this.config.aplayerJs.startsWith("https://")) ? this.config.aplayerJs : this.processUrl(this.config.aplayerJs) : "https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"; // 默认 CDN // 处理 MetingJS 路径 - 修改这里 this.metingJsPath = this.config.meting?.jsPath ? (this.config.meting.jsPath.startsWith("http://") || this.config.meting.jsPath.startsWith("https://")) ? this.config.meting.jsPath : this.processUrl(this.config.meting.jsPath) : "https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"; // 默认 CDN // 预处理本地音乐列表(保持不变) this.processedLocalPlaylist = this.config.mode === "local" && this.config.local?.playlist ? this.config.local.playlist.map((song) => ({ ...song, url: this.processUrl(song.url), cover: song.cover ? this.processUrl(song.cover) : undefined, })) : null; } /** * 处理 URL 路径 * @param {string} path - 原始路径 * @returns {string} 处理后的完整 URL */ processUrl(path) { if (!path.startsWith('http')) { return this.joinUrl("", this.getBaseUrl(), path); } else { return path; } } /** * 获取基础 URL * @returns {string} 基础 URL */ getBaseUrl() { // 从 window 获取 if (typeof window !== 'undefined') { return window.location.pathname.includes('/') ? window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/') + 1) : ''; } return ''; } /** * 拼接 URL * @param {...string} parts - URL 部分 * @returns {string} 拼接后的 URL */ joinUrl(...parts) { const joined = parts.join("/"); return joined.replace(/\/+/g, "/"); } /** * 加载 CSS 文件 */ loadCSS() { // 检查是否已经加载过 if (document.querySelector(`link[href="${this.aplayerCssPath}"]`)) { return; } const link = document.createElement("link"); link.rel = "stylesheet"; link.href = this.aplayerCssPath; document.head.appendChild(link); } /** * 加载 APlayer JS * @returns {Promise} Promise 对象 */ loadAPlayer() { return new Promise((resolve) => { if (window.APlayer) { this.loadedAPlayer = true; resolve(window.APlayer); return; } const script = document.createElement("script"); script.src = this.aplayerJsPath; script.async = true; script.onload = () => { this.loadedAPlayer = true; resolve(window.APlayer); }; script.onerror = () => { console.error("Failed to load APlayer"); resolve(null); }; document.head.appendChild(script); }); } /** * 加载 MetingJS * @returns {Promise} Promise 对象 */ loadMetingJS() { return new Promise((resolve) => { if (window.customElements?.get("meting-js")) { this.loadedMetingJS = true; resolve(true); return; } const script = document.createElement("script"); script.src = this.metingJsPath; script.async = true; script.onload = () => { this.loadedMetingJS = true; // 如果配置了全局 API,设置它 if (this.config.mode === "meting" && this.config.meting?.api) { window.meting_api = this.config.meting.api; } this.setupMetingElements(); resolve(true); }; script.onerror = () => { console.error("Failed to load MetingJS"); resolve(false); }; document.head.appendChild(script); }); } /** * 设置 Meting 元素 */ setupMetingElements() { setTimeout(() => { const metingElements = document.querySelectorAll('meting-js'); metingElements.forEach((element) => { this.setupAPlayerContainer(element.aplayer); }); }, 100); } /** * 设置 APlayer 容器 * @param {Object} aplayer - APlayer 实例 */ setupAPlayerContainer(aplayer) { if (!aplayer) return; const checkAPlayer = setInterval(() => { if (aplayer && aplayer.container) { clearInterval(checkAPlayer); aplayer.container.setAttribute('data-positioned', 'right'); requestAnimationFrame(() => { if (aplayer.container) { aplayer.container.style.right = '0'; aplayer.container.style.left = 'unset'; setTimeout(() => { aplayer.container.setAttribute('data-initialized', 'true'); // 如果配置了默认隐藏歌词,则隐藏歌词 if (this.config.player?.lrcHidden && aplayer.lrc) { aplayer.lrc.hide(); const lrcButton = aplayer.container.querySelector('.aplayer-icon-lrc'); if (lrcButton) { lrcButton.classList.add('aplayer-icon-lrc-inactivity'); } } // 处理自动播放 if (this.config.player?.autoplay && aplayer.paused) { this.tryAutoplay(aplayer); } }, 200); } }); } }, 50); setTimeout(() => clearInterval(checkAPlayer), 10000); } /** * 尝试自动播放 * @param {Object} aplayer - APlayer 实例 */ tryAutoplay(aplayer) { if (!this.config.player?.autoplay) return; if (this.hasMusicInteracted) { if (aplayer && aplayer.paused) { const playPromise = aplayer.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch((error) => { if (error.name !== 'NotAllowedError') { console.log('自动播放失败:', error.name); } }); } } } else { const enableAutoplay = () => { if (this.hasMusicInteracted) return; this.hasMusicInteracted = true; if (aplayer && aplayer.paused) { const playPromise = aplayer.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch((error) => { if (error.name !== 'NotAllowedError') { console.log('自动播放失败:', error.name); } }); } } }; ['click', 'keydown', 'touchstart', 'scroll'].forEach(eventType => { document.addEventListener(eventType, enableAutoplay, { once: true, passive: true }); }); } } /** * 创建播放器容器 */ createContainer() { // 检查容器是否已存在 let container = document.getElementById(`#${this.playId}`); if (container) { return container; } container = document.createElement('div'); container.id = this.playId; if (this.config.responsive?.mobile?.hide) { container.classList.add('mobile-hide'); } document.body.appendChild(container); return container; } /** * 渲染 Meting 模式播放器 * @param {HTMLElement} container - 容器元素 */ renderMetingPlayer(container) { if (this.config.mode !== "meting" || !this.config.meting) return; const metingElement = document.createElement('meting-js'); metingElement.setAttribute('server', this.config.meting.server || "netease"); metingElement.setAttribute('type', this.config.meting.type || "playlist"); metingElement.setAttribute('id', this.config.meting.id || ""); if (this.config.meting.api) { metingElement.setAttribute('api', this.config.meting.api); } if (this.config.meting.auth) { metingElement.setAttribute('auth', this.config.meting.auth); } // 设置播放器属性 this.setPlayerAttributes(metingElement); container.appendChild(metingElement); } /** * 渲染本地模式播放器 * @param {HTMLElement} container - 容器元素 */ renderLocalPlayer(container) { if (this.config.mode !== "local" || !this.processedLocalPlaylist || this.processedLocalPlaylist.length === 0) { return; } if (this.processedLocalPlaylist.length === 1) { const song = this.processedLocalPlaylist[0]; const metingElement = document.createElement('meting-js'); metingElement.setAttribute('name', song.name || ""); metingElement.setAttribute('artist', song.artist || ""); metingElement.setAttribute('url', song.url || ""); if (song.cover) { metingElement.setAttribute('cover', song.cover); } // 设置播放器属性 this.setPlayerAttributes(metingElement, song.lrc ? "2" : "0"); // 添加歌词 if (song.lrc) { const lrcElement = document.createElement('pre'); lrcElement.hidden = true; lrcElement.textContent = song.lrc; metingElement.appendChild(lrcElement); } container.appendChild(metingElement); } else { const localPlayerDiv = document.createElement('div'); localPlayerDiv.id = 'local-aplayer'; container.appendChild(localPlayerDiv); } } /** * 设置播放器属性 * @param {HTMLElement} element - 元素 * @param {string} lrcType - 歌词类型 */ setPlayerAttributes(element, lrcType = null) { const playerConfig = this.config.player || {}; element.setAttribute('fixed', (playerConfig.fixed ?? true) ? "true" : "false"); element.setAttribute('mini', (playerConfig.mini ?? true) ? "true" : "false"); element.setAttribute('autoplay', playerConfig.autoplay ? "true" : "false"); element.setAttribute('theme', playerConfig.theme || "#b7daff"); element.setAttribute('loop', playerConfig.loop || "all"); element.setAttribute('order', playerConfig.order || "list"); element.setAttribute('preload', playerConfig.preload || "auto"); element.setAttribute('volume', String(playerConfig.volume ?? 0.7)); element.setAttribute('mutex', playerConfig.mutex !== false ? "true" : "false"); element.setAttribute('lrc-type', lrcType !== null ? lrcType : String(playerConfig.lrcType ?? 3)); element.setAttribute('list-folded', playerConfig.listFolded ? "true" : "false"); element.setAttribute('list-max-height', playerConfig.listMaxHeight || "340px"); element.setAttribute('storage-name', playerConfig.storageName || "aplayer-setting"); } /** * 初始化本地播放器(多个音乐) */ async initLocalPlayer() { if ( this.config.mode !== "local" || !this.processedLocalPlaylist || this.processedLocalPlaylist.length <= 1 ) { return; } const APlayerClass = await this.loadAPlayer(); if (!APlayerClass) return; const container = document.getElementById("local-aplayer"); if (!container) return; const audioList = this.processedLocalPlaylist.map((song) => ({ name: song.name, artist: song.artist, url: song.url, cover: song.cover, lrc: song.lrc || undefined, type: "auto", })); const aplayerOptions = { container: container, audio: audioList, mutex: this.config.player?.mutex !== false, lrcType: this.config.player?.lrcType !== undefined ? this.config.player.lrcType : 0, fixed: this.config.player?.fixed ?? true, mini: this.config.player?.mini ?? true, autoplay: this.config.player?.autoplay || false, theme: this.config.player?.theme || "#b7daff", loop: this.config.player?.loop || "all", order: this.config.player?.order || "list", preload: this.config.player?.preload || "auto", volume: this.config.player?.volume || 0.7, listFolded: this.config.player?.listFolded || false, listMaxHeight: this.config.player?.listMaxHeight || "340px", storageName: this.config.player?.storageName || "aplayer-setting", }; try { const aplayer = new APlayerClass(aplayerOptions); this.setupAPlayerContainer(aplayer); } catch (error) { console.error("Failed to initialize APlayer:", error); } } /** * 添加样式 */ addStyles() { const style = document.createElement('style'); style.textContent = ` #${this.playId} { position: relative; z-index: 1000; } #${this.playId} .aplayer-fixed { z-index: 9999; } #${this.playId} .aplayer.aplayer-fixed { animation: none !important; } #${this.playId} .aplayer.aplayer-fixed .aplayer-body { animation: none !important; transition: none !important; } #${this.playId} .aplayer.aplayer-fixed[data-positioned="right"] { right: 0 !important; left: unset !important; } @media (max-width: 768px) { #${this.playId}.mobile-hide { display: none !important; } } `; document.head.appendChild(style); } /** * 初始化播放器 */ async initPlayer() { // 创建容器 const container = this.createContainer(); // 添加样式 this.addStyles(); // 根据模式渲染播放器 if (this.config.mode === "meting") { this.renderMetingPlayer(container); await this.loadAPlayer(); await this.loadMetingJS(); } else if (this.config.mode === "local") { this.renderLocalPlayer(container); if (this.processedLocalPlaylist && this.processedLocalPlaylist.length > 1) { // 多个音乐,使用 APlayer if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => this.initLocalPlayer()); } else { await this.initLocalPlayer(); } } else { // 单个音乐,使用 MetingJS await this.loadAPlayer(); await this.loadMetingJS(); } } } /** * 销毁播放器 */ destroy() { const container = document.getElementById(`#${this.playId}`); if (container) { container.remove(); } // 移除样式 const styles = document.querySelectorAll('style'); styles.forEach(style => { if (style.textContent.includes(`#${this.playId}`)) { style.remove(); } }); } } // 创建全局实例 const musicPlayer = new MusicPlayerPlugin(); /** * 初始化音乐播放器 * @param {Object} config - 播放器配置 */ function initMusicPlayer(config) { musicPlayer.init(config); } /** * 销毁音乐播放器 */ function destroyMusicPlayer() { musicPlayer.destroy(); } // 为了兼容性,同时支持CommonJS和浏览器全局变量 if (typeof module !== 'undefined' && module.exports) { module.exports = { init: initMusicPlayer, destroy: destroyMusicPlayer }; } if (typeof window !== 'undefined') { window.MusicPlayer = { init: initMusicPlayer, destroy: destroyMusicPlayer }; } }));