无额外的js包,纯html-js-css实现

使用HTML5 <audio> 元素,可以处理flac音频的播放

支持循环播放,支持无损格式音频播放,支持用户选择音频路径进行播放

需要与文件服务器连接!需要上一个nginx服务器的配置!(nginx配置中的默认方法可能要用inline,教程中的默认方法是空)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>音频播放器</title>
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
        }
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
        }
        #file-manager {
            flex: 1;
            overflow-y: auto;
            border-right: 1px solid #ccc;
            padding: 10px;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
        }
        #search-container {
            margin-bottom: 10px;
        }
        #search-input {
            width: 100%;
            padding: 5px;
            box-sizing: border-box;
        }
        #file-list {
            flex: 1;
            overflow-y: auto;
        }
        #player {
            padding: 10px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background-color: #f0f0f0;
        }
        .file-item, .folder-item {
            cursor: pointer;
            padding: 5px;
            margin: 2px 0;
            display: flex;
            align-items: center;
        }
        .file-item:hover, .folder-item:hover {
            background-color: #f0f0f0;
        }
        .folder-item::before {
            content: '📁 ';
        }
        .file-item::before {
            content: '🎵 ';
        }
        .file-name {
            max-width: 200px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        #audio-player {
            width: 100%;
            max-width: 500px;
        }
        #now-playing {
            margin-top: 5px;
            font-weight: bold;
            text-align: center;
        }

        /* 响应式设计 */
        @media screen and (min-width: 768px) {
            body {
                flex-direction: row;
            }
            #file-manager {
                width: 15%;
                height: 100%;
                flex: none;
            }
            #player {
                width: calc(100% - 300px);
            }
        }

        @media screen and (max-width: 767px) {
            body {
                flex-direction: column;
            }
            #file-manager {
                flex: 1;
                width: 100%;
            }
            #player {
                height: 80px;
            }
        }
    </style>
</head>
<body>
    <div id="file-manager">
        <div id="search-container">
            <input type="text" id="search-input" placeholder="搜索文件...">
        </div>
        <div id="file-list"></div>
    </div>
    <div id="player">
        <audio id="audio-player" controls></audio>
        <div id="now-playing"></div>
    </div>

    <script>
        // 将baseUrl替换为占位符
        const baseUrl = 'YOUR_SERVER_URL_HERE';
        let currentPath = '/';
        let playlist = [];
        let currentTrack = 0;
        let currentItems = [];

        async function fetchDirectory(path) {
            const response = await fetch(`${baseUrl}/files-list${path}`, {
                headers: {
                    'Authorization': 'Basic ' + btoa('YOUR_USERNAME:YOUR_PASSWORD')
                }
            });
            const text = await response.text();
            const parser = new DOMParser();
            const doc = parser.parseFromString(text, 'text/html');
            return Array.from(doc.querySelectorAll('pre a')).slice(1);
        }

        async function loadDirectory(path) {
            currentPath = path;
            currentItems = await fetchDirectory(path);
            displayItems(currentItems);
            updatePlaylist();
        }

        function displayItems(items) {
            const fileList = document.getElementById('file-list');
            fileList.innerHTML = '';

            if (currentPath !== '/') {
                const backItem = createItemElement('..', true);
                backItem.onclick = () => {
                    const pathParts = currentPath.split('/').filter(Boolean);
                    pathParts.pop();
                    loadDirectory('/' + pathParts.join('/') + '/');
                };
                fileList.appendChild(backItem);
            }

            items.forEach(item => {
                const fullFileName = item.getAttribute('href');
                const fileName = decodeURIComponent(fullFileName.replace(/\/$/, ''));
                const isDirectory = fullFileName.endsWith('/');

                const itemElement = createItemElement(fileName, isDirectory);

                if (isDirectory) {
                    itemElement.onclick = () => loadDirectory(currentPath + fullFileName);
                } else if (fileName.match(/\.(mp3|wav|ogg|flac)$/i)) {
                    itemElement.onclick = () => playAudio(currentPath + fullFileName, fileName);
                }

                fileList.appendChild(itemElement);
            });
        }

        function createItemElement(name, isDirectory) {
            const itemElement = document.createElement('div');
            itemElement.className = isDirectory ? 'folder-item' : 'file-item';

            const nameSpan = document.createElement('span');
            nameSpan.className = 'file-name';
            nameSpan.textContent = name;
            nameSpan.title = name;
            itemElement.appendChild(nameSpan);

            return itemElement;
        }

        function playAudio(filePath, fileName) {
            const audioPlayer = document.getElementById('audio-player');
            const nowPlaying = document.getElementById('now-playing');
            audioPlayer.src = `${baseUrl}/files-get${filePath}`;
            audioPlayer.play();
            nowPlaying.textContent = `正在播放: ${fileName}`;
            currentTrack = playlist.findIndex(item => item.path === filePath);
        }

        function playNext() {
            currentTrack = (currentTrack + 1) % playlist.length;
            const nextTrack = playlist[currentTrack];
            playAudio(nextTrack.path, nextTrack.name);
        }

        function updatePlaylist() {
            playlist = currentItems.filter(item => item.getAttribute('href').match(/\.(mp3|wav|ogg|flac)$/i))
                                   .map(item => ({
                                       path: currentPath + item.getAttribute('href'),
                                       name: decodeURIComponent(item.getAttribute('href').replace(/\/$/, ''))
                                   }));
        }

        function searchItems(query) {
            const filteredItems = currentItems.filter(item => {
                const fileName = decodeURIComponent(item.getAttribute('href').replace(/\/$/, ''));
                return fileName.toLowerCase().includes(query.toLowerCase());
            });
            displayItems(filteredItems);
        }

        document.getElementById('audio-player').addEventListener('ended', playNext);

        document.getElementById('search-input').addEventListener('input', (e) => {
            searchItems(e.target.value);
        });

        // Initialize
        loadDirectory('/');
    </script>
</body>
</html>

demo:http://193.43.94.240/player/