无额外的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>
评论区