【免费】本站同款音乐播放器

本站同款音乐播放器免费开源

还在因网站播放器是收费的而烦恼?

还在因播放器歌曲太难添加而耗时间?

还在因某些音乐是收费的导致不能播放而痛苦?

现在我将开源本站的音乐播放器,理论上是适用所有网站的,可以添加到自己的其他网站试试

本播放器基于MettingApi作为核心驱动,所以即使这个MettingApi的地址失效了,还可以换其他的(搜索引擎一搜一大堆),例如如果https://music.3e0.cn/   这个api失效了,只需要搜索https://music.3e0.cn/   并替换成其他api例如https://xxx.com

歌单ID也可以换

本播放器修复了大量BUG,已经很稳定了

遇到其他网站和本网站(blog.3e0.cn)一模一的播放器都是出自本网站所开源的

在下面代码中播放器的歌单是网易云热歌榜

 

以下是代码

<style>
.meting-player-container * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Microsoft YaHei', sans-serif;
}

.meting-player-container {
    position: fixed;
    left: 20px;
    bottom: 70px;
    z-index: 1000;
    width: 360px;
    transition: all 0.3s ease;
}

.meting-player-container.minimized {
    width: 50px;
    height: 50px;
}

.meting-player-main {
    background: rgba(60, 66, 64, 0.9);
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    color: white;
    display: flex;
    flex-direction: column;
    transition: all 0.3s ease;
}

.minimized .meting-player-main {
    height: 50px;
    width: 50px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.meting-album-cover {
    display: none;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    animation: meting-rotate 10s linear infinite;
    animation-play-state: paused;
}

.minimized .meting-album-cover {
    display: block;
}

.playing .meting-album-cover {
    animation-play-state: running;
}

@keyframes meting-rotate {
    100% {
        transform: rotate(360deg);
    }
}

.meting-player-header {
    display: flex;
    align-items: center;
    padding: 12px 15px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.minimized .meting-player-header {
    display: none;
}

.meting-song-info {
    flex: 1;
    overflow: hidden;
}

.meting-song-title {
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.meting-song-artist {
    font-size: 12px;
    opacity: 0.8;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.meting-player-controls {
    display: flex;
    flex-direction: column;
    padding: 15px;
}

.minimized .meting-player-controls {
    display: none;
}

.meting-progress-top {
    display: flex;
    align-items: center;
    margin-bottom: 15px;
    width: 100%;
}

.meting-progress-container {
    flex: 1;
    height: 12px;
    background: rgba(255, 255, 255, 0.3);
    border-radius: 6px;
    margin-right: 10px;
    cursor: pointer;
    position: relative;
}

.meting-progress-bar {
    height: 100%;
    background: #1db954;
    border-radius: 6px;
    width: 0%;
}

.meting-time-display {
    font-size: 12px;
    opacity: 0.8;
    min-width: 80px;
    text-align: center;
}

.meting-controls-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
}

.meting-control-buttons {
    display: flex;
    align-items: center;
}

.meting-control-btn,
.meting-volume-btn,
.meting-toggle-player,
.meting-lyrics-btn,
.meting-playlist-btn,
.meting-mode-btn {
    -webkit-tap-highlight-color: transparent;
    outline: none;
}

.meting-control-btn {
    background: none;
    border: none;
    color: white;
    font-size: 20px;
    cursor: pointer;
    margin: 0 5px;
    transition: all 0.2s;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.meting-control-btn:hover {
    color: #1db954;
}

.meting-play-pause {
    font-size: 32px;
    width: 40px;
    height: 40px;
}

.meting-volume-container {
    display: flex;
    align-items: center;
}

.meting-volume-btn {
    background: none;
    border: none;
    color: white;
    font-size: 18px;
    cursor: pointer;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.meting-volume-slider {
    width: 80px;
    margin-left: 5px;
    -webkit-appearance: none;
    height: 4px;
    background: rgba(255, 255, 255, 0.3);
    border-radius: 2px;
    outline: none;
    -webkit-tap-highlight-color: transparent;
}

.meting-volume-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
    cursor: pointer;
}

.meting-toggle-player {
    position: absolute;
    top: -10px;
    right: -10px;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #1db954;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
    border: none;
    font-size: 14px;
    transition: all 0.2s;
}

.meting-toggle-player:hover {
    transform: scale(1.1);
}

.meting-lyrics-container {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    text-align: center;
    padding: 10px;
    background: rgba(0, 0, 0, 0.5);
    color: white;
    font-size: 16px;
    z-index: 900;
    transition: all 0.3s;
    line-height: 1.5;
}

.meting-lyrics-line {
    margin: 5px 0;
    opacity: 0.5;
    transition: all 0.3s;
}

.meting-lyrics-line.active {
    opacity: 1;
    color: #1db954;
    font-weight: bold;
    transform: scale(1.05);
}

.meting-playlist-btn {
    background: none;
    border: none;
    color: white;
    font-size: 20px;
    cursor: pointer;
    margin-left: 10px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.meting-lyrics-btn {
    background: none;
    border: none;
    color: white;
    font-size: 16px;
    cursor: pointer;
    margin-left: 10px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
}

.meting-lyrics-btn.active {
    color: #1db954;
}

/* 重新设计播放列表样式 - 更美观的电脑端播放列表 */
.meting-playlist-container {
    position: absolute;
    bottom: calc(100% + 10px);
    left: 0;
    right: 0;
    background: rgba(60, 66, 64, 0.9);
    backdrop-filter: blur(10px);
    border-radius: 12px;
    max-height: 400px;
    overflow-y: auto;
    display: none;
    padding: 15px;
    box-shadow: 0 -5px 25px rgba(0, 0, 0, 0.4);
    border: 1px solid rgba(255, 255, 255, 0.1);
    scrollbar-width: thin;
    scrollbar-color: #1db954 rgba(255, 255, 255, 0.1);
}

.meting-playlist-container::-webkit-scrollbar {
    width: 6px;
}

.meting-playlist-container::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.05);
    border-radius: 3px;
}

.meting-playlist-container::-webkit-scrollbar-thumb {
    background-color: #1db954;
    border-radius: 3px;
}
/* 本播放器来源blog.3e0.cn 使用请保留本标识 */
.meting-playlist-container.show {
    display: block;
}

.meting-playlist-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
    padding-bottom: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.meting-playlist-title {
    font-size: 16px;
    font-weight: bold;
    color: #fff;
}

.meting-playlist-count {
    font-size: 13px;
    opacity: 0.7;
}

.meting-playlist-items {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.meting-playlist-item {
    padding: 12px 15px;
    border-radius: 8px;
    cursor: pointer;
    display: flex;
    align-items: center;
    transition: all 0.2s ease;
    background: rgba(255, 255, 255, 0.03);
    -webkit-tap-highlight-color: transparent;
    outline: none;
}

.meting-playlist-item:hover {
    background: rgba(29, 185, 84, 0.15);
    transform: translateY(-2px);
}

.meting-playlist-item.playing {
    background: rgba(29, 185, 84, 0.2);
    box-shadow: 0 4px 12px rgba(29, 185, 84, 0.2);
}

.meting-playlist-item.playing .meting-playlist-item-index {
    color: #1db954;
    font-weight: bold;
}

.meting-playlist-item.playing .meting-playlist-item-title {
    color: #1db954;
}

.meting-playlist-item-index {
    margin-right: 15px;
    font-size: 14px;
    opacity: 0.8;
    min-width: 24px;
    text-align: center;
    transition: all 0.2s ease;
}

.meting-playlist-item-info {
    flex: 1;
    overflow: hidden;
    min-width: 0;
}

.meting-playlist-item-title {
    font-size: 14px;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 3px;
    transition: all 0.2s ease;
}

.meting-playlist-item-artist {
    font-size: 12px;
    opacity: 0.7;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.meting-playlist-item-duration {
    font-size: 12px;
    opacity: 0.6;
    margin-left: 10px;
    min-width: 40px;
    text-align: right;
}

.meting-mode-btn {
    background: none;
    border: none;
    color: white;
    font-size: 16px;
    cursor: pointer;
    margin-left: 5px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
}

@media screen and (max-width: 768px) {
    .meting-player-container {
        left: 10px;
        right: auto;
        bottom: 20px;
        width: auto;
        max-width: 320px;
        margin: 0;
    }
    
    .meting-player-main {
        border-radius: 10px;
    }
    
    .meting-player-header {
        padding: 10px;
    }
    
    .meting-song-title {
        font-size: 13px;
    }
    
    .meting-song-artist {
        font-size: 11px;
    }
    
    .meting-player-controls {
        padding: 10px;
    }
    
    .meting-progress-top {
        flex-direction: row;
        align-items: center;
        margin-bottom: 10px;
    }
    
    .meting-time-display {
        margin-top: 0;
        min-width: auto;
        text-align: right;
        font-size: 11px;
        margin-left: 10px;
    }
    
    .meting-controls-bottom {
        flex-direction: row;
        gap: 5px;
    }
    
    .meting-volume-container {
        width: auto;
        justify-content: flex-end;
    }
    
    .meting-volume-slider {
        flex: 1;
        max-width: 80px;
    }
    
    .meting-control-btn {
        font-size: 18px;
        margin: 0 3px;
        width: 28px;
        height: 28px;
    }
    
    .meting-play-pause {
        font-size: 28px;
        width: 36px;
        height: 36px;
    }
    
    .meting-toggle-player {
        top: -8px;
        right: -8px;
        width: 20px;
        height: 20px;
        font-size: 12px;
    }
    
    .meting-lyrics-container {
        font-size: 14px;
        padding: 8px;
        z-index: 800;
        line-height: 1.5;
    }
    
    /* 手机端播放列表样式 */
    .meting-playlist-container {
        max-height: 200px;
        border-radius: 10px;
        padding: 10px;
        bottom: calc(100% + 8px);
        background: rgba(60, 66, 64, 0.9);
    }
    
    .meting-playlist-header {
        margin-bottom: 10px;
        padding-bottom: 8px;
    }
    
    .meting-playlist-title {
        font-size: 14px;
    }
    
    .meting-playlist-count {
        font-size: 11px;
    }
    
    .meting-playlist-item {
        padding: 10px 12px;
        border-radius: 6px;
    }
    
    .meting-playlist-item-index {
        margin-right: 12px;
        font-size: 12px;
        min-width: 20px;
    }
    
    .meting-playlist-item-title {
        font-size: 13px;
    }
    
    .meting-playlist-item-artist {
        font-size: 11px;
    }
    
    .meting-playlist-item-duration {
        font-size: 11px;
        min-width: 35px;
    }
    
    .meting-lyrics-container:not([style*="display: none"]) {
        height: auto;
    }
    
    .meting-lyrics-container[style*="display: none"] ~ .meting-player-container {
        bottom: 10px;
    }
}

/* 小屏手机适配 - 修改为左下角位置 */
@media screen and (max-width: 480px) {
    .meting-player-container {
        left: 5px;
        right: auto;
        bottom: 10px;
        max-width: 300px;
    }
/* 这个播放器来源blog.3e0.cn 使用请保留本标识 */    
    .meting-player-header {
        padding: 8px;
    }
    
    .meting-song-title {
        font-size: 12px;
    }
    
    .meting-song-artist {
        font-size: 10px;
    }
    
    .meting-control-btn {
        font-size: 16px;
        margin: 0 2px;
        width: 24px;
        height: 24px;
    }
    
    .meting-play-pause {
        font-size: 24px;
        width: 32px;
        height: 32px;
    }
    
    .meting-lyrics-btn,
    .meting-playlist-btn,
    .meting-mode-btn {
        font-size: 16px;
        margin-left: 5px;
        width: 20px;
        height: 20px;
    }
    
    .meting-time-display {
        font-size: 10px;
    }
    
    .meting-lyrics-container {
        font-size: 13px;
        padding: 5px;
        line-height: 1.5;
    }
    
    .meting-volume-slider {
        max-width: 60px;
    }
    
    /* 小屏手机播放列表样式 */
    .meting-playlist-container {
        max-height: 180px;
        padding: 8px;
        bottom: calc(100% + 5px);
        border-radius: 8px;
        background: rgba(60, 66, 64, 0.9);
    }
    
    .meting-playlist-item {
        padding: 8px 10px;
    }
    
    .meting-playlist-item-index {
        margin-right: 10px;
        font-size: 11px;
        min-width: 18px;
    }
    
    .meting-playlist-item-title {
        font-size: 12px;
    }
    
    .meting-playlist-item-artist {
        font-size: 10px;
    }
    
    .meting-playlist-item-duration {
        display: none;
    }
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div class="meting-player-container">
    <div class="meting-player-main">
        <img class="meting-album-cover" src="" alt="专辑封面">
        <div class="meting-player-header">
            <div class="meting-song-info">
                <div class="meting-song-title">正在加载...</div>
                <div class="meting-song-artist">请稍候</div>
            </div>
            <button class="meting-lyrics-btn">词</button>
            <button class="meting-playlist-btn"><i class="fa fa-list"></i></button>
            <button class="meting-mode-btn"><i class="fa fa-random"></i></button>
        </div>
        <div class="meting-player-controls">
            <div class="meting-progress-top">
                <div class="meting-progress-container">
                    <div class="meting-progress-bar"></div>
                </div>
                <div class="meting-time-display">00:00 / 00:00</div>
            </div>
            <div class="meting-controls-bottom">
                <div class="meting-control-buttons">
                    <button class="meting-control-btn meting-prev-btn"><i class="fa fa-step-backward"></i></button>
                    <button class="meting-control-btn meting-play-pause"><i class="fa fa-play"></i></button>
                    <button class="meting-control-btn meting-next-btn"><i class="fa fa-step-forward"></i></button>
                </div>
                <div class="meting-volume-container">
                    <button class="meting-volume-btn"><i class="fa fa-volume-up"></i></button>
                    <input type="range" class="meting-volume-slider" min="0" max="100" value="80">
                </div>
            </div>
        </div>
        <div class="meting-playlist-container">
            <div class="meting-playlist-header">
                <div class="meting-playlist-title">播放列表</div>
                <div class="meting-playlist-count">0 首歌曲</div>
            </div>
            <div class="meting-playlist-items"></div>
        </div>
    </div>
    <button class="meting-toggle-player"><i class="fa fa-minus"></i></button>
</div>

<div class="meting-lyrics-container">
    <div class="meting-lyrics-line">歌词加载中...</div>
</div>

<script>
(function() {
    // 播放器状态
    const playerState = {
        playlist: [],
        currentIndex: 0,
        isPlaying: false,
        volume: 80,
        currentTime: 0,
        duration: 0,
        mode: 'random',
        minimized: false,
        lyricsVisible: true,
        lyricsAutoHide: true,
        lastPlayedSong: null,
        lastPlayedPosition: 0,
        autoPlayBlocked: false,
        lyrics: []
    };

    // DOM元素
    const elements = {
        playerContainer: document.querySelector('.meting-player-container'),
        playerMain: document.querySelector('.meting-player-main'),
        albumCover: document.querySelector('.meting-album-cover'),
        songTitle: document.querySelector('.meting-song-title'),
        songArtist: document.querySelector('.meting-song-artist'),
        playPauseBtn: document.querySelector('.meting-play-pause'),
        prevBtn: document.querySelector('.meting-prev-btn'),
        nextBtn: document.querySelector('.meting-next-btn'),
        progressContainer: document.querySelector('.meting-progress-container'),
        progressBar: document.querySelector('.meting-progress-bar'),
        timeDisplay: document.querySelector('.meting-time-display'),
        volumeBtn: document.querySelector('.meting-volume-btn'),
        volumeSlider: document.querySelector('.meting-volume-slider'),
        togglePlayerBtn: document.querySelector('.meting-toggle-player'),
        lyricsContainer: document.querySelector('.meting-lyrics-container'),
        lyricsLine: document.querySelector('.meting-lyrics-line'),
        playlistBtn: document.querySelector('.meting-playlist-btn'),
        playlistContainer: document.querySelector('.meting-playlist-container'),
        playlistItems: document.querySelector('.meting-playlist-items'),
        playlistTitle: document.querySelector('.meting-playlist-title'),
        playlistCount: document.querySelector('.meting-playlist-count'),
        modeBtn: document.querySelector('.meting-mode-btn'),
        lyricsBtn: document.querySelector('.meting-lyrics-btn')
    };

    // 音频元素
    const audio = new Audio();
    audio.volume = playerState.volume / 100;

    // 新增:HTML实体解码函数,解决&apos;显示问题
    function decodeHtmlEntities(text) {
        const textArea = document.createElement('textarea');
        textArea.innerHTML = text;
        return textArea.value;
    }

    // 从localStorage加载状态
    function loadPlayerState() {
        const savedState = localStorage.getItem('metingPlayerState');
        if (savedState) {
            const state = JSON.parse(savedState);
            playerState.currentIndex = state.currentIndex || 0;
            playerState.isPlaying = state.isPlaying || false;
            playerState.volume = state.volume || 80;
            playerState.currentTime = state.currentTime || 0;
            playerState.mode = state.mode || 'random';
            playerState.minimized = state.minimized || false;
            playerState.lyricsVisible = state.lyricsVisible !== undefined ? state.lyricsVisible : true;
            playerState.lyricsAutoHide = state.lyricsAutoHide !== undefined ? state.lyricsAutoHide : true;
            playerState.lastPlayedSong = state.lastPlayedSong || null;
            playerState.lastPlayedPosition = state.lastPlayedPosition || 0;
            
            // 应用保存的状态
            audio.volume = playerState.volume / 100;
            elements.volumeSlider.value = playerState.volume;
            
            if (playerState.minimized) {
                elements.playerContainer.classList.add('minimized');
                elements.togglePlayerBtn.innerHTML = '<i class="fa fa-plus"></i>';
            }
            
            // 强制同步歌词按钮和显示状态
            updateLyricsButton();
            updateLyricsVisibility();
            
            updateModeButton();
            updateVolumeIcon();
        }
    }

    // 保存状态到localStorage
    function savePlayerState() {
        playerState.currentTime = audio.currentTime;
        
        if (playerState.playlist.length > 0 && playerState.currentIndex >= 0) {
            const currentSong = playerState.playlist[playerState.currentIndex];
            playerState.lastPlayedSong = {
                id: currentSong.id || currentSong.name,
                name: currentSong.name,
                artist: currentSong.artist
            };
            playerState.lastPlayedPosition = audio.currentTime;
        }
        
        localStorage.setItem('metingPlayerState', JSON.stringify(playerState));
    }

    // 更新歌词按钮状态
    function updateLyricsButton() {
        if (playerState.lyricsVisible) {
            elements.lyricsBtn.classList.add('active');
        } else {
            elements.lyricsBtn.classList.remove('active');
        }
    }

    // 修复歌词开启后播放不显示的问题:简化判断逻辑,确保播放时优先显示
    function updateLyricsVisibility() {
        // 只要歌词开启、未被自动播放阻止,且音频有资源,播放时就显示
        const canShowLyrics = playerState.lyricsVisible && !playerState.autoPlayBlocked && audio.src;
        if (playerState.isPlaying && canShowLyrics) {
            elements.lyricsContainer.style.display = 'block';
            adjustPlayerPosition();
        } else {
            elements.lyricsContainer.style.display = 'none';
            resetPlayerPosition();
        }
    }

    // 调整播放器位置(确保在歌词上方)
    function adjustPlayerPosition() {
        if (window.matchMedia('(max-width: 768px)').matches) {
            const lyricsHeight = elements.lyricsContainer.offsetHeight;
            elements.playerContainer.style.bottom = (lyricsHeight + 10) + 'px';
        }
    }

    // 重置播放器位置(歌词隐藏时)
    function resetPlayerPosition() {
        if (window.matchMedia('(max-width: 768px)').matches) {
            elements.playerContainer.style.bottom = '10px';
        }
    }

    // 更新模式按钮
    function updateModeButton() {
        switch(playerState.mode) {
            case 'random':
                elements.modeBtn.innerHTML = '<i class="fa fa-random"></i>';
                break;
            case 'sequential':
                elements.modeBtn.innerHTML = '<i class="fa fa-arrow-right"></i>';
                break;
            case 'loop':
                elements.modeBtn.innerHTML = '<i class="fa fa-repeat"></i>';
                break;
        }
    }

    // 更新音量图标
    function updateVolumeIcon() {
        const volume = elements.volumeSlider.value;
        let iconClass = 'fa-volume-up';
        
        if (volume == 0) {
            iconClass = 'fa-volume-off';
        } else if (volume < 33) {
            iconClass = 'fa-volume-down';
        }
        
        elements.volumeBtn.innerHTML = `<i class="fa ${iconClass}"></i>`;
    }

    // 从MetingAPI获取播放列表
    async function fetchPlaylist() {
        try {
            const response = await fetch('https://music.3e0.cn/?server=netease&type=playlist&id=3778678');
            const playlist = await response.json();
            
            if (Array.isArray(playlist) && playlist.length > 0) {
                playerState.playlist = playlist;
                renderPlaylist();
                
                // 检查是否有上次播放的歌曲记录
                let targetIndex = 0;
                let targetPosition = 0;
                
                if (playerState.lastPlayedSong) {
                    const foundIndex = playlist.findIndex(song => 
                        (song.id && song.id === playerState.lastPlayedSong.id) || 
                        (song.name === playerState.lastPlayedSong.name && song.artist === playerState.lastPlayedSong.artist)
                    );
                    
                    if (foundIndex !== -1) {
                        targetIndex = foundIndex;
                        targetPosition = playerState.lastPlayedPosition;
                    }
                } else if (playerState.currentIndex >= 0 && playerState.currentIndex < playlist.length) {
                    targetIndex = playerState.currentIndex;
                    targetPosition = playerState.currentTime;
                }
                
                loadSong(targetIndex, targetPosition);
                
                if (playerState.isPlaying) {
                    setTimeout(() => {
                        audio.play().catch(e => {
                            console.log('自动播放被阻止:', e);
                            playerState.autoPlayBlocked = true;
                            playerState.isPlaying = false;
                            updatePlayButtonState();
                            savePlayerState();
                        });
                    }, 500);
                }
            } else {
                console.error('获取播放列表失败');
                elements.songTitle.textContent = '获取播放列表失败';
            }
        } catch (error) {
            console.error('获取播放列表出错:', error);
            elements.songTitle.textContent = '获取播放列表出错';
        }
    }

    // 渲染播放列表
    function renderPlaylist() {
        elements.playlistItems.innerHTML = '';
        elements.playlistCount.textContent = `${playerState.playlist.length} 首歌曲`;
        
        playerState.playlist.forEach((song, index) => {
            const item = document.createElement('div');
            item.className = 'meting-playlist-item';
            if (index === playerState.currentIndex) {
                item.classList.add('playing');
            }//本播放器来自blog.3e0.cn 使用请保留本标识
            
            let durationText = '';
            if (song.duration) {
                const minutes = Math.floor(song.duration / 60);
                const seconds = Math.floor(song.duration % 60);
                durationText = `${minutes}:${seconds.toString().padStart(2, '0')}`;
            }
            
            item.innerHTML = `
                <div class="meting-playlist-item-index">${index + 1}</div>
                <div class="meting-playlist-item-info">
                    <div class="meting-playlist-item-title">${decodeHtmlEntities(song.name)}</div>
                    <div class="meting-playlist-item-artist">${decodeHtmlEntities(song.artist)}</div>
                </div>
                <div class="meting-playlist-item-duration">${durationText}</div>
            `;
            
            item.addEventListener('click', () => {
                playerState.isPlaying = true;
                playerState.autoPlayBlocked = false;
                updatePlayButtonState();
                updateLyricsVisibility();
                
                loadSong(index);
                audio.play().catch(e => {
                    console.log('播放被阻止:', e);
                    playerState.autoPlayBlocked = true;
                    playerState.isPlaying = false;
                    updatePlayButtonState();
                    updateLyricsVisibility();
                    savePlayerState();
                });
            });
            
            elements.playlistItems.appendChild(item);
        });
        
        if (elements.playlistContainer.classList.contains('show')) {
            scrollToCurrentSong();
        }
    }

    // 滚动到当前播放的歌曲
    function scrollToCurrentSong() {
        const currentItem = elements.playlistItems.children[playerState.currentIndex];
        if (currentItem) {
            currentItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
        }
    }

    // 加载歌曲
    function loadSong(index, position = 0) {
        if (index < 0 || index >= playerState.playlist.length) return;
        
        const song = playerState.playlist[index];
        playerState.currentIndex = index;
        
        // 解码歌曲名和歌手名中的HTML实体
        elements.songTitle.textContent = decodeHtmlEntities(song.name);
        elements.songArtist.textContent = decodeHtmlEntities(song.artist);
        
        if (song.pic) {
            elements.albumCover.src = song.pic;
        }
        
        audio.src = song.url;
        audio.load();
        
        audio.onloadedmetadata = function() {
            if (position > 0 && position < audio.duration) {
                audio.currentTime = position;
                playerState.currentTime = position;
            }
            
            updatePlayButtonState();
            if (playerState.isPlaying) {
                audio.play().catch(e => {
                    console.log('播放被阻止:', e);
                    playerState.autoPlayBlocked = true;
                    playerState.isPlaying = false;
                    updatePlayButtonState();
                    updateLyricsVisibility();
                    savePlayerState();
                });
            }
        };
        
        fetchLyrics(song.lrc);
        
        const items = document.querySelectorAll('.meting-playlist-item');
        items.forEach((item, i) => {
            if (i === index) {
                item.classList.add('playing');
            } else {
                item.classList.remove('playing');
            }
        });
        
        scrollToCurrentSong();
        savePlayerState();
        updateLyricsVisibility();
        updateLyricsButton();
    }

    // 获取歌词
    async function fetchLyrics(url) {
        try {
            const response = await fetch(url);
            const text = await response.text();
            parseLyrics(text);
        } catch (error) {
            console.error('获取歌词失败:', error);
            elements.lyricsLine.textContent = '歌词加载失败';
            adjustPlayerPosition();
        }
    }

    // 解析歌词 - 应用HTML实体解码
    function parseLyrics(text) {
        const lines = text.split('\n');
        const lyrics = [];
        const regex = /\[(\d+):(\d+)\.(\d+)\](.*)/;
        
        lines.forEach(line => {
            const match = line.match(regex);
            if (match) {
                const minutes = parseInt(match[1]);
                const seconds = parseInt(match[2]);
                const milliseconds = parseInt(match[3]);
                // 解码歌词文本中的HTML实体
                const text = decodeHtmlEntities(match[4].trim());
                const time = minutes * 60 + seconds + milliseconds / 100;
                lyrics.push({ time, text });
            }
        });
        
        lyrics.sort((a, b) => a.time - b.time);
        playerState.lyrics = lyrics;
        elements.lyricsLine.textContent = lyrics.length > 0 ? lyrics[0].text : '暂无歌词';
        adjustPlayerPosition();
    }

    // 更新歌词显示
    function updateLyrics() {
        if (!playerState.lyrics || !playerState.lyricsVisible || playerState.autoPlayBlocked || !playerState.isPlaying) return;
        
        const currentTime = audio.currentTime;
        let currentLine = '';
        
        for (let i = playerState.lyrics.length - 1; i >= 0; i--) {
            if (currentTime >= playerState.lyrics[i].time) {
                currentLine = playerState.lyrics[i].text;
                break;
            }
        }
        
        elements.lyricsLine.textContent = currentLine || '♪';
        adjustPlayerPosition();
    }

    // 同步播放按钮状态
    function updatePlayButtonState() {
        if (playerState.isPlaying) {
            elements.playPauseBtn.innerHTML = '<i class="fa fa-pause"></i>';
            elements.playerMain.classList.add('playing');
        } else {
            elements.playPauseBtn.innerHTML = '<i class="fa fa-play"></i>';
            elements.playerMain.classList.remove('playing');
        }
    }

    // 修复播放后歌词不显示:播放/暂停时立即同步状态
    function togglePlay() {
        if (audio.paused) {
            audio.play().then(() => {
                playerState.isPlaying = true;
                playerState.autoPlayBlocked = false;
            }).catch(e => {
                console.log('播放被阻止:', e);
                playerState.autoPlayBlocked = true;
                playerState.isPlaying = false;
            });
        } else {
            audio.pause();
            playerState.isPlaying = false;
        }
        // 立即更新状态,不等待异步结果
        setTimeout(() => {
            updatePlayButtonState();
            updateLyricsVisibility();
            savePlayerState();
        }, 0);
    }

    // 切换歌词显示/隐藏
    function toggleLyrics() {
        playerState.lyricsVisible = !playerState.lyricsVisible;
        updateLyricsVisibility();
        updateLyricsButton();
        savePlayerState();
    }

    // 下一首
    function nextSong() {
        let nextIndex;
        switch(playerState.mode) {
            case 'random':
                nextIndex = Math.floor(Math.random() * playerState.playlist.length);
                break;
            case 'sequential':
                nextIndex = (playerState.currentIndex + 1) % playerState.playlist.length;
                break;
            case 'loop':
                nextIndex = playerState.currentIndex;
                break;
        }
        
        playerState.isPlaying = true;
        playerState.autoPlayBlocked = false;
        updatePlayButtonState();
        updateLyricsVisibility();
        
        loadSong(nextIndex);
        audio.play().catch(e => {
            console.log('播放被阻止:', e);
            playerState.autoPlayBlocked = true;
            playerState.isPlaying = false;
            updatePlayButtonState();
            updateLyricsVisibility();
            savePlayerState();
        });
    }

    // 上一首
    function prevSong() {
        let prevIndex;
        switch(playerState.mode) {
            case 'random':
                prevIndex = Math.floor(Math.random() * playerState.playlist.length);
                break;
            case 'sequential':
                prevIndex = (playerState.currentIndex - 1 + playerState.playlist.length) % playerState.playlist.length;
                break;
            case 'loop':
                prevIndex = playerState.currentIndex;
                break;
        }
        
        playerState.isPlaying = true;
        playerState.autoPlayBlocked = false;
        updatePlayButtonState();
        updateLyricsVisibility();
        
        loadSong(prevIndex);
        audio.play().catch(e => {
            console.log('播放被阻止:', e);
            playerState.autoPlayBlocked = true;
            playerState.isPlaying = false;
            updatePlayButtonState();
            updateLyricsVisibility();
            savePlayerState();
        });
    }

    // 切换播放模式
    function toggleMode() {
        const modes = ['random', 'sequential', 'loop'];
        const currentIndex = modes.indexOf(playerState.mode);
        playerState.mode = modes[(currentIndex + 1) % modes.length];
        updateModeButton();
        savePlayerState();
    }

    // 切换播放器最小化状态
    function toggleMinimize() {
        playerState.minimized = !playerState.minimized;
        
        if (playerState.minimized) {
            elements.playerContainer.classList.add('minimized');
            elements.togglePlayerBtn.innerHTML = '<i class="fa fa-plus"></i>';
            elements.playlistContainer.classList.remove('show');
        } else {
            elements.playerContainer.classList.remove('minimized');
            elements.togglePlayerBtn.innerHTML = '<i class="fa fa-minus"></i>';
        }
        
        savePlayerState();
    }

    // 切换播放列表显示
    function togglePlaylist() {
        if (playerState.minimized) {
            toggleMinimize();
        }
        elements.playlistContainer.classList.toggle('show');
        
        if (elements.playlistContainer.classList.contains('show')) {
            setTimeout(() => {
                scrollToCurrentSong();
            }, 0);
        }
    }

    // 更新进度条
    function updateProgress() {
        const percent = (audio.currentTime / audio.duration) * 100;
        elements.progressBar.style.width = `${percent}%`;
        
        const currentMinutes = Math.floor(audio.currentTime / 60);
        const currentSeconds = Math.floor(audio.currentTime % 60);
        const durationMinutes = Math.floor(audio.duration / 60);
        const durationSeconds = Math.floor(audio.duration % 60);
        
        elements.timeDisplay.textContent = 
            `${currentMinutes.toString().padStart(2, '0')}:${currentSeconds.toString().padStart(2, '0')} / ${durationMinutes.toString().padStart(2, '0')}:${durationSeconds.toString().padStart(2, '0')}`;
        
        updateLyrics();
        
        if (Math.floor(audio.currentTime) % 5 === 0) {
            savePlayerState();
        }
    }

    // 设置进度
    function setProgress(e) {
        const width = elements.progressContainer.clientWidth;
        const clickX = e.offsetX;
        const duration = audio.duration;
        
        audio.currentTime = (clickX / width) * duration;
        savePlayerState();
    }

    // 设置音量
    function setVolume() {
        const volume = elements.volumeSlider.value;
        audio.volume = volume / 100;
        playerState.volume = volume;
        
        updateVolumeIcon();
        savePlayerState();
    }

    // 初始化事件监听
    function initEventListeners() {
        elements.playPauseBtn.addEventListener('click', togglePlay);
        elements.prevBtn.addEventListener('click', prevSong);
        elements.nextBtn.addEventListener('click', nextSong);
        elements.progressContainer.addEventListener('click', setProgress);
        elements.volumeSlider.addEventListener('input', setVolume);
        elements.togglePlayerBtn.addEventListener('click', toggleMinimize);
        elements.lyricsBtn.addEventListener('click', toggleLyrics);
        elements.playlistBtn.addEventListener('click', togglePlaylist);
        elements.modeBtn.addEventListener('click', toggleMode);
        
        audio.addEventListener('timeupdate', updateProgress);
        audio.addEventListener('ended', nextSong);
        
        // 监听音频原生事件,确保状态100%同步
        audio.addEventListener('play', () => {
            playerState.isPlaying = true;
            updatePlayButtonState();
            updateLyricsVisibility();
        });
        audio.addEventListener('pause', () => {
            playerState.isPlaying = false;
            updatePlayButtonState();
            updateLyricsVisibility();
        });
        
        window.addEventListener('beforeunload', savePlayerState);
        document.addEventListener('visibilitychange', function() {
            if (document.hidden) {
                savePlayerState();
            }
        });
        
        window.addEventListener('resize', function() {
            updateLyricsVisibility();
        });

        const lyricsObserver = new MutationObserver(() => {
            adjustPlayerPosition();
        });
        lyricsObserver.observe(elements.lyricsLine, { childList: true, characterData: true, subtree: true });
    }

    // 初始化播放器
    function initPlayer() {
        loadPlayerState();
        initEventListeners();
        fetchPlaylist();
        updatePlayButtonState();
        updateLyricsButton();
    }

    // 启动播放器
    initPlayer();
})();
</script>

 

 

温馨提示: 本文最后更新于2025-12-26 23:35:31,某些文章具有时效性,若有错误或已失效,请在下方 留言或联系 梦想的博客
© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容