/** * 音乐播放器 - 外置应用案例 * 展示了如何创建一个功能完整的外置应用 */ class MusicPlayer { constructor() { // 应用状态 this.isPlaying = false; this.currentTrackIndex = 0; this.playlist = []; this.isShuffleMode = false; this.repeatMode = 'none'; // none, one, all this.volume = 0.7; // DOM 元素 this.audioPlayer = null; this.playPauseBtn = null; this.progressBar = null; this.volumeBar = null; this.playlist_element = null; // 系统SDK this.systemSDK = null; this.init(); } /** * 初始化应用 */ async init() { console.log('[音乐播放器] 初始化开始'); // 等待DOM加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.setupApp()); } else { this.setupApp(); } // 初始化系统SDK await this.initSystemSDK(); } /** * 初始化系统SDK */ async initSystemSDK() { try { console.log('[音乐播放器] 开始初始化系统SDK'); // 等待系统SDK可用 let attempts = 0; const maxAttempts = 100; // 增加尝试次数 while (!window.SystemSDK && attempts < maxAttempts) { console.log(`[音乐播放器] 等待SystemSDK可用... (尝试 ${attempts + 1}/${maxAttempts})`); await new Promise(resolve => setTimeout(resolve, 100)); attempts++; } if (window.SystemSDK) { console.log('[音乐播放器] SystemSDK对象已找到,开始初始化'); // 初始化SDK const initResult = await window.SystemSDK.init({ appId: 'music-player', appName: '音乐播放器', version: '1.0.0', permissions: ['storage.read', 'storage.write'] }); if (initResult.success) { this.systemSDK = window.SystemSDK; console.log('[音乐播放器] 系统SDK初始化成功'); // 显示系统通知 if (this.systemSDK.ui) { try { await this.systemSDK.ui.showNotification({ title: '音乐播放器', message: '应用已启动,准备播放音乐!', type: 'info' }); } catch (error) { console.warn('[音乐播放器] 显示通知失败:', error); } } // 从本地存储恢复播放列表 await this.loadPlaylistFromStorage(); } else { console.error('[音乐播放器] 系统SDK初始化失败:', initResult.error); } } else { console.error('[音乐播放器] 系统SDK不可用,已达到最大尝试次数'); } } catch (error) { console.error('[音乐播放器] 系统SDK初始化失败:', error); } } /** * 设置应用界面和事件 */ setupApp() { console.log('[音乐播放器] 设置界面'); // 获取DOM元素 this.audioPlayer = document.getElementById('audioPlayer'); this.playPauseBtn = document.getElementById('playPauseBtn'); this.progressBar = document.getElementById('progressBar'); this.volumeBar = document.getElementById('volumeBar'); this.playlist_element = document.getElementById('playlist'); // 设置音频事件 this.setupAudioEvents(); // 设置控制按钮事件 this.setupControlEvents(); // 设置窗口控制事件 this.setupWindowControls(); // 设置键盘快捷键 this.setupKeyboardShortcuts(); // 初始化音量 this.setVolume(this.volume * 100); console.log('[音乐播放器] 应用设置完成'); } /** * 设置音频播放器事件 */ setupAudioEvents() { if (!this.audioPlayer) return; // 播放开始 this.audioPlayer.addEventListener('play', () => { this.isPlaying = true; this.updatePlayButton(); this.updateStatus('正在播放'); }); // 播放暂停 this.audioPlayer.addEventListener('pause', () => { this.isPlaying = false; this.updatePlayButton(); this.updateStatus('已暂停'); }); // 播放结束 this.audioPlayer.addEventListener('ended', () => { this.handleTrackEnded(); }); // 时间更新 this.audioPlayer.addEventListener('timeupdate', () => { this.updateProgress(); }); // 加载完成 this.audioPlayer.addEventListener('loadedmetadata', () => { this.updateTotalTime(); }); // 加载错误 this.audioPlayer.addEventListener('error', (e) => { console.error('[音乐播放器] 播放错误:', e); this.updateStatus('播放出错'); this.nextTrack(); }); } /** * 设置控制按钮事件 */ setupControlEvents() { // 播放/暂停 document.getElementById('playPauseBtn')?.addEventListener('click', () => { this.togglePlayPause(); }); // 上一曲 document.getElementById('prevBtn')?.addEventListener('click', () => { this.prevTrack(); }); // 下一曲 document.getElementById('nextBtn')?.addEventListener('click', () => { this.nextTrack(); }); // 随机播放 document.getElementById('shuffleBtn')?.addEventListener('click', () => { this.toggleShuffle(); }); // 重复播放 document.getElementById('repeatBtn')?.addEventListener('click', () => { this.toggleRepeat(); }); // 进度条 this.progressBar?.addEventListener('input', () => { this.seekTo(this.progressBar.value); }); // 音量控制 this.volumeBar?.addEventListener('input', () => { this.setVolume(this.volumeBar.value); }); // 文件选择 document.getElementById('addFilesBtn')?.addEventListener('click', () => { document.getElementById('fileInput').click(); }); document.getElementById('fileInput')?.addEventListener('change', (e) => { this.handleFileSelection(e.target.files); }); // 清空播放列表 document.getElementById('clearPlaylistBtn')?.addEventListener('click', () => { this.clearPlaylist(); }); } /** * 设置窗口控制事件 */ setupWindowControls() { // 最小化 document.getElementById('minimizeBtn')?.addEventListener('click', () => { if (this.systemSDK) { this.systemSDK.window.minimize(); } }); // 最大化/还原 document.getElementById('maximizeBtn')?.addEventListener('click', () => { if (this.systemSDK) { this.systemSDK.window.toggleMaximize(); } }); // 关闭 document.getElementById('closeBtn')?.addEventListener('click', () => { if (this.systemSDK) { this.systemSDK.window.close(); } else { window.close(); } }); } /** * 设置键盘快捷键 */ setupKeyboardShortcuts() { document.addEventListener('keydown', (e) => { if (e.target.tagName === 'INPUT') return; switch (e.code) { case 'Space': e.preventDefault(); this.togglePlayPause(); break; case 'ArrowLeft': e.preventDefault(); this.prevTrack(); break; case 'ArrowRight': e.preventDefault(); this.nextTrack(); break; case 'ArrowUp': e.preventDefault(); this.setVolume(Math.min(100, this.volume * 100 + 5)); break; case 'ArrowDown': e.preventDefault(); this.setVolume(Math.max(0, this.volume * 100 - 5)); break; } }); } /** * 处理文件选择 */ handleFileSelection(files) { const audioFiles = Array.from(files).filter(file => file.type.startsWith('audio/') ); if (audioFiles.length === 0) { this.updateStatus('未选择音频文件'); return; } audioFiles.forEach(file => { const track = { id: Date.now() + Math.random(), name: file.name.replace(/\.[^/.]+$/, ""), file: file, url: URL.createObjectURL(file), duration: 0 }; this.playlist.push(track); }); this.updatePlaylist(); this.savePlaylistToStorage(); this.updateStatus(`添加了 ${audioFiles.length} 首歌曲`); // 如果是第一次添加歌曲,自动播放 if (this.playlist.length === audioFiles.length) { this.loadTrack(0); } } /** * 播放/暂停切换 */ togglePlayPause() { if (!this.audioPlayer || this.playlist.length === 0) { this.updateStatus('播放列表为空'); return; } if (this.isPlaying) { this.audioPlayer.pause(); } else { this.audioPlayer.play().catch(error => { console.error('[音乐播放器] 播放失败:', error); this.updateStatus('播放失败'); }); } } /** * 上一曲 */ prevTrack() { if (this.playlist.length === 0) return; let newIndex; if (this.isShuffleMode) { newIndex = Math.floor(Math.random() * this.playlist.length); } else { newIndex = this.currentTrackIndex - 1; if (newIndex < 0) { newIndex = this.playlist.length - 1; } } this.loadTrack(newIndex); } /** * 下一曲 */ nextTrack() { if (this.playlist.length === 0) return; let newIndex; if (this.isShuffleMode) { newIndex = Math.floor(Math.random() * this.playlist.length); } else { newIndex = this.currentTrackIndex + 1; if (newIndex >= this.playlist.length) { newIndex = 0; } } this.loadTrack(newIndex); } /** * 加载指定曲目 */ loadTrack(index) { if (index < 0 || index >= this.playlist.length) return; this.currentTrackIndex = index; const track = this.playlist[index]; this.audioPlayer.src = track.url; this.updateCurrentTrackInfo(track); this.updatePlaylistHighlight(); if (this.isPlaying) { this.audioPlayer.play().catch(error => { console.error('[音乐播放器] 播放失败:', error); }); } } /** * 处理曲目播放结束 */ handleTrackEnded() { switch (this.repeatMode) { case 'one': this.audioPlayer.currentTime = 0; this.audioPlayer.play(); break; case 'all': this.nextTrack(); break; default: if (this.currentTrackIndex < this.playlist.length - 1) { this.nextTrack(); } else { this.isPlaying = false; this.updatePlayButton(); this.updateStatus('播放完成'); } break; } } /** * 切换随机播放 */ toggleShuffle() { this.isShuffleMode = !this.isShuffleMode; const btn = document.getElementById('shuffleBtn'); if (btn) { btn.classList.toggle('active', this.isShuffleMode); } this.updateStatus(this.isShuffleMode ? '随机播放已开启' : '随机播放已关闭'); } /** * 切换重复播放模式 */ toggleRepeat() { const modes = ['none', 'one', 'all']; const currentIndex = modes.indexOf(this.repeatMode); this.repeatMode = modes[(currentIndex + 1) % modes.length]; const btn = document.getElementById('repeatBtn'); if (btn) { btn.classList.toggle('active', this.repeatMode !== 'none'); switch (this.repeatMode) { case 'one': btn.textContent = '🔂'; break; case 'all': btn.textContent = '🔁'; break; default: btn.textContent = '🔁'; break; } } const modeNames = { none: '关闭', one: '单曲循环', all: '列表循环' }; this.updateStatus(`重复播放: ${modeNames[this.repeatMode]}`); } /** * 设置音量 */ setVolume(value) { this.volume = value / 100; if (this.audioPlayer) { this.audioPlayer.volume = this.volume; } const volumeValue = document.getElementById('volumeValue'); if (volumeValue) { volumeValue.textContent = `${Math.round(value)}%`; } if (this.volumeBar) { this.volumeBar.value = value; } } /** * 跳转到指定时间 */ seekTo(percentage) { if (this.audioPlayer && this.audioPlayer.duration) { const time = (percentage / 100) * this.audioPlayer.duration; this.audioPlayer.currentTime = time; } } /** * 更新播放按钮状态 */ updatePlayButton() { if (this.playPauseBtn) { this.playPauseBtn.textContent = this.isPlaying ? '⏸️' : '▶️'; this.playPauseBtn.classList.toggle('playing', this.isPlaying); } } /** * 更新进度条 */ updateProgress() { if (this.audioPlayer && this.progressBar && this.audioPlayer.duration) { const progress = (this.audioPlayer.currentTime / this.audioPlayer.duration) * 100; this.progressBar.value = progress; const currentTime = document.getElementById('currentTime'); if (currentTime) { currentTime.textContent = this.formatTime(this.audioPlayer.currentTime); } } } /** * 更新总时长显示 */ updateTotalTime() { if (this.audioPlayer) { const totalTime = document.getElementById('totalTime'); if (totalTime) { totalTime.textContent = this.formatTime(this.audioPlayer.duration || 0); } } } /** * 更新当前曲目信息 */ updateCurrentTrackInfo(track) { const titleElement = document.getElementById('trackTitle'); const artistElement = document.getElementById('trackArtist'); const albumElement = document.getElementById('trackAlbum'); if (titleElement) titleElement.textContent = track.name; if (artistElement) artistElement.textContent = '未知艺术家'; if (albumElement) albumElement.textContent = '未知专辑'; } /** * 更新播放列表显示 */ updatePlaylist() { if (!this.playlist_element) return; if (this.playlist.length === 0) { this.playlist_element.innerHTML = '