🤍 酷咔智能媒体播放器

使用方法总结
输入链接:粘贴音频或视频文件链接
加载媒体:点击"加载媒体"按钮
控制播放:使用播放控制按钮
调节设置:调整音量和播放速度
全屏播放:视频模式下可全屏观看
音频播放
支持MP3、WAV、OGG、M4A等格式
音量控制
视频播放
支持MP4、WebM、OGG等格式
视频画面正确显示
全屏播放功能
播放进度控制
代码分享
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>酷咔智能媒体播放器</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #1a237e 0%, #4a148c 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.player-container {
width: 100%;
max-width: 800px;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.player-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.player-title {
font-size: 2rem;
font-weight: 600;
margin-bottom: 10px;
}
.player-subtitle {
font-size: 1.1rem;
opacity: 0.9;
}
.player-body {
padding: 30px;
}
.input-section {
margin-bottom: 30px;
}
.input-label {
display: block;
margin-bottom: 10px;
font-weight: 500;
color: #333;
}
.url-input {
width: 100%;
padding: 12px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 1rem;
margin-bottom: 15px;
}
.url-input:focus {
outline: none;
border-color: #667eea;
}
.action-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.btn {
padding: 12px 20px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
flex: 1;
}
.load-btn {
background: #4CAF50;
color: white;
}
.load-btn:hover {
background: #388e3c;
}
.load-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.fullscreen-btn {
background: #2196F3;
color: white;
}
.fullscreen-btn:hover:not(:disabled) {
background: #0b7dda;
}
.fullscreen-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* 媒体容器 - 关键修复 */
.media-container {
width: 100%;
background: #000;
border-radius: 10px;
overflow: hidden;
margin-bottom: 20px;
position: relative;
display: none; /* 默认隐藏,加载后显示 */
}
.media-container.active {
display: block;
}
/* 视频元素样式 - 确保正确显示 */
#videoElement {
width: 100%;
height: auto;
max-height: 400px;
display: block; /* 确保block显示 */
background: black; /* 黑色背景避免空白 */
}
/* 音频容器样式 */
.audio-container {
width: 100%;
height: 100px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.2rem;
}
.audio-container.hidden {
display: none;
}
.video-placeholder {
width: 100%;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
background: #111;
color: white;
font-size: 1.2rem;
}
.video-placeholder.hidden {
display: none;
}
.controls {
display: flex;
gap: 10px;
margin: 20px 0;
}
.control-btn {
padding: 12px;
border: none;
border-radius: 8px;
background: #f5f5f5;
color: #333;
cursor: pointer;
flex: 1;
font-size: 1.1rem;
}
.control-btn:hover:not(:disabled) {
background: #e0e0e0;
}
.control-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.progress-container {
margin: 20px 0;
}
.progress-bar {
height: 6px;
background: #e0e0e0;
border-radius: 3px;
cursor: pointer;
position: relative;
}
.progress-fill {
height: 100%;
background: #667eea;
width: 0%;
border-radius: 3px;
position: absolute;
top: 0;
left: 0;
}
.time-display {
display: flex;
justify-content: space-between;
color: #666;
font-size: 0.9rem;
margin-top: 5px;
}
.volume-control {
display: flex;
align-items: center;
gap: 10px;
margin: 15px 0;
}
.volume-slider {
flex: 1;
height: 6px;
-webkit-appearance: none;
background: #e0e0e0;
border-radius: 3px;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
}
.status {
padding: 15px;
background: #f8f9ff;
border-radius: 8px;
text-align: center;
margin-top: 20px;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.status.success {
color: #4CAF50;
}
.status.error {
color: #f44336;
}
.status.info {
color: #666;
}
.status.warning {
color: #FF9800;
}
.debug-info {
margin-top: 20px;
padding: 10px;
background: #f5f5f5;
border-radius: 5px;
font-family: monospace;
font-size: 0.9rem;
color: #666;
}
.example-links {
margin: 10px 0;
font-size: 0.9rem;
color: #666;
}
.example-link {
color: #667eea;
text-decoration: none;
margin: 0 5px;
cursor: pointer;
}
.example-link:hover {
text-decoration: underline;
}
.media-type-badge {
display: inline-block;
padding: 4px 12px;
background: #4CAF50;
color: white;
border-radius: 20px;
font-size: 0.9rem;
margin-left: 10px;
}
.media-type-badge.video {
background: #2196F3;
}
.media-type-badge.audio {
background: #FF9800;
}
</style>
</head>
<body>
<div class="player-container">
<div class="player-header">
<h1 class="player-title">酷咔智能媒体播放器</h1>
<p class="player-subtitle">https://5blog.cn/tx/media-player.html</p>
</div>
<div class="player-body">
<div class="input-section">
<label class="input-label">媒体链接:</label>
<input type="text" class="url-input" id="urlInput"
placeholder="粘贴MP3、MP4、WebM等链接"
value="https://5blog.cn/content/uploadfile/202602/31641771082987.mp4">
<div class="action-buttons">
<button class="btn load-btn" id="loadBtn">加载媒体</button>
<button class="btn fullscreen-btn" id="fullscreenBtn" disabled>全屏</button>
</div>
<div class="example-links">
测试链接:
<a class="example-link" onclick="setExample('video')">视频测试</a>
<a class="example-link" onclick="setExample('audio')">音频测试</a>
</div>
</div>
<!-- 媒体显示容器 -->
<div class="media-container" id="mediaContainer">
<!-- 视频元素 - 直接内嵌 -->
<video id="videoElement" controls style="display: none;"></video>
<!-- 音频占位符 -->
<div id="audioContainer" class="audio-container hidden">
🎵 音频播放中
</div>
<!-- 视频占位符(视频未显示时显示) -->
<div id="videoPlaceholder" class="video-placeholder">
视频加载中...
</div>
</div>
<!-- 媒体类型指示器 -->
<div style="text-align: center; margin: 10px 0;">
当前模式: <span id="mediaTypeIndicator" class="media-type-badge">未加载</span>
</div>
<div class="progress-container">
<div class="progress-bar" id="progressBar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div class="time-display">
<span id="currentTime">0:00</span>
<span id="duration">0:00</span>
</div>
</div>
<div class="controls">
<button class="control-btn" id="playBtn" disabled>▶ 播放</button>
<button class="control-btn" id="pauseBtn" disabled>⏸ 暂停</button>
<button class="control-btn" id="stopBtn" disabled>⏹ 停止</button>
<button class="control-btn" id="rewindBtn" disabled>⏪ 10秒</button>
<button class="control-btn" id="forwardBtn" disabled>⏩ 10秒</button>
</div>
<div class="volume-control">
<span>🔊</span>
<input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="70">
<span id="volumeValue">70%</span>
</div>
<div class="status info" id="statusText">等待加载媒体...</div>
<div class="debug-info" id="debugInfo" style="display: none;">
调试信息: <span id="debugText">暂无</span>
</div>
</div>
</div>
<script>
class FixedMediaPlayer {
constructor() {
this.mediaElement = null;
this.isVideo = false;
this.isLoaded = false;
this.isPlaying = false;
// DOM元素
this.elements = {
// 输入相关
urlInput: document.getElementById('urlInput'),
loadBtn: document.getElementById('loadBtn'),
fullscreenBtn: document.getElementById('fullscreenBtn'),
// 媒体容器相关
mediaContainer: document.getElementById('mediaContainer'),
videoElement: document.getElementById('videoElement'),
audioContainer: document.getElementById('audioContainer'),
videoPlaceholder: document.getElementById('videoPlaceholder'),
// 控制相关
playBtn: document.getElementById('playBtn'),
pauseBtn: document.getElementById('pauseBtn'),
stopBtn: document.getElementById('stopBtn'),
rewindBtn: document.getElementById('rewindBtn'),
forwardBtn: document.getElementById('forwardBtn'),
// 进度和音量相关
progressBar: document.getElementById('progressBar'),
progressFill: document.getElementById('progressFill'),
currentTime: document.getElementById('currentTime'),
duration: document.getElementById('duration'),
volumeSlider: document.getElementById('volumeSlider'),
volumeValue: document.getElementById('volumeValue'),
// 状态和信息相关
statusText: document.getElementById('statusText'),
mediaTypeIndicator: document.getElementById('mediaTypeIndicator'),
debugInfo: document.getElementById('debugInfo'),
debugText: document.getElementById('debugText')
};
this.init();
}
init() {
console.log('播放器初始化...');
this.logDebug('播放器初始化开始');
// 绑定事件
this.elements.loadBtn.addEventListener('click', () => this.loadMedia());
this.elements.urlInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.loadMedia();
});
this.elements.playBtn.addEventListener('click', () => this.play());
this.elements.pauseBtn.addEventListener('click', () => this.pause());
this.elements.stopBtn.addEventListener('click', () => this.stop());
this.elements.rewindBtn.addEventListener('click', () => this.seek(-10));
this.elements.forwardBtn.addEventListener('click', () => this.seek(10));
this.elements.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
this.elements.progressBar.addEventListener('click', (e) => this.seekToPosition(e));
this.elements.volumeSlider.addEventListener('input', (e) => this.setVolume(e.target.value));
this.logDebug('事件监听器设置完成');
// 设置初始音量
this.setVolume(80);
}
logDebug(message) {
console.log('[Debug]', message);
this.elements.debugText.textContent = message;
this.elements.debugInfo.style.display = 'block';
}
setStatus(message, type = 'info') {
this.elements.statusText.textContent = message;
this.elements.statusText.className = 'status ' + type;
this.logDebug(`状态: ${message}`);
}
detectMediaType(url) {
const extension = url.split('.').pop().toLowerCase().split('?')[0];
const videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'wmv', 'flv', 'mkv'];
const audioExtensions = ['mp3', 'wav', 'ogg', 'm4a', 'aac', 'flac'];
if (videoExtensions.includes(extension)) {
return 'video';
} else if (audioExtensions.includes(extension)) {
return 'audio';
} else {
// 从URL推测
if (url.includes('/video/') || url.includes('.mp4')) {
return 'video';
}
return 'audio';
}
}
loadMedia() {
const url = this.elements.urlInput.value.trim();
if (!url) {
this.setStatus('请输入媒体链接', 'error');
return;
}
this.logDebug(`开始加载: ${url}`);
this.setStatus('正在检测媒体类型...', 'info');
// 重置播放器
this.resetPlayer();
// 检测媒体类型
this.isVideo = this.detectMediaType(url) === 'video';
this.logDebug(`检测为: ${this.isVideo ? '视频' : '音频'}`);
// 设置媒体元素
this.setupMediaElement();
// 更新界面指示器
this.updateMediaTypeIndicator();
// 显示媒体容器
this.elements.mediaContainer.classList.add('active');
// 设置媒体源
this.mediaElement.src = url;
this.mediaElement.load();
this.elements.loadBtn.disabled = true;
this.setStatus('正在加载媒体...', 'info');
}
setupMediaElement() {
// 使用已有的video元素
this.mediaElement = this.elements.videoElement;
// 清理之前的事件监听器
const newVideo = this.mediaElement.cloneNode();
this.mediaElement.parentNode.replaceChild(newVideo, this.mediaElement);
this.mediaElement = newVideo;
this.mediaElement.id = 'videoElement';
// 设置样式
this.mediaElement.style.width = '100%';
this.mediaElement.style.height = 'auto';
this.mediaElement.style.display = 'block'; // 关键修复:确保显示
this.mediaElement.style.background = '#000';
// 如果是音频,隐藏视频控件
if (!this.isVideo) {
this.mediaElement.style.display = 'none';
this.elements.audioContainer.classList.remove('hidden');
this.elements.videoPlaceholder.classList.add('hidden');
} else {
this.mediaElement.style.display = 'block'; // 关键修复
this.elements.audioContainer.classList.add('hidden');
this.elements.videoPlaceholder.classList.add('hidden'); // 隐藏占位符
}
// 设置事件监听
this.setupMediaEventListeners();
this.logDebug(`媒体元素设置完成 (${this.isVideo ? '视频' : '音频'})`);
}
setupMediaEventListeners() {
this.mediaElement.addEventListener('loadedmetadata', () => this.onLoadedMetadata());
this.mediaElement.addEventListener('loadeddata', () => this.onLoadedData());
this.mediaElement.addEventListener('canplay', () => this.onCanPlay());
this.mediaElement.addEventListener('error', (e) => this.onError(e));
this.mediaElement.addEventListener('timeupdate', () => this.updateProgress());
this.mediaElement.addEventListener('ended', () => this.onEnded());
this.mediaElement.addEventListener('play', () => this.onPlay());
this.mediaElement.addEventListener('pause', () => this.onPause());
this.logDebug('媒体事件监听器已设置');
}
onLoadedMetadata() {
this.logDebug(`元数据加载完成,时长: ${this.formatTime(this.mediaElement.duration)}`);
this.updateDuration();
if (this.isVideo) {
this.logDebug(`视频尺寸: ${this.mediaElement.videoWidth}x${this.mediaElement.videoHeight}`);
}
}
onLoadedData() {
this.isLoaded = true;
this.elements.playBtn.disabled = false;
this.elements.pauseBtn.disabled = false;
this.elements.stopBtn.disabled = false;
this.elements.rewindBtn.disabled = false;
this.elements.forwardBtn.disabled = false;
this.elements.fullscreenBtn.disabled = !this.isVideo;
this.setStatus('媒体加载完成,可以播放', 'success');
this.logDebug('媒体数据加载完成');
}
onCanPlay() {
this.logDebug('媒体可以开始播放');
// 如果是视频,确保视频元素可见
if (this.isVideo) {
this.mediaElement.style.display = 'block';
this.elements.videoPlaceholder.classList.add('hidden');
}
}
onError(event) {
console.error('媒体错误:', this.mediaElement.error);
let errorMessage = '未知错误';
if (this.mediaElement.error) {
switch(this.mediaElement.error.code) {
case this.mediaElement.error.MEDIA_ERR_ABORTED:
errorMessage = '加载被中止';
break;
case this.mediaElement.error.MEDIA_ERR_NETWORK:
errorMessage = '网络错误';
break;
case this.mediaElement.error.MEDIA_ERR_DECODE:
errorMessage = '解码错误';
break;
case this.mediaElement.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
errorMessage = '格式不支持';
break;
default:
errorMessage = `错误代码: ${this.mediaElement.error.code}`;
}
}
this.setStatus(`加载失败: ${errorMessage}`, 'error');
this.logDebug(`媒体错误: ${errorMessage}`);
// 如果是视频,显示占位符
if (this.isVideo) {
this.elements.videoPlaceholder.classList.remove('hidden');
this.elements.videoPlaceholder.textContent = '加载失败: ' + errorMessage;
}
}
onEnded() {
this.isPlaying = false;
this.setStatus('播放完成', 'success');
this.logDebug('播放结束');
}
onPlay() {
this.isPlaying = true;
this.logDebug('播放开始');
}
onPause() {
this.isPlaying = false;
this.logDebug('播放暂停');
}
play() {
if (!this.isLoaded) {
this.setStatus('媒体未加载', 'error');
return;
}
this.mediaElement.play().then(() => {
this.logDebug('播放成功');
this.setStatus('播放中...', 'success');
}).catch(error => {
console.error('播放失败:', error);
this.setStatus(`播放失败: ${error.message}`, 'error');
this.logDebug(`播放失败: ${error.message}`);
});
}
pause() {
this.mediaElement.pause();
this.setStatus('已暂停', 'info');
}
stop() {
this.mediaElement.pause();
this.mediaElement.currentTime = 0;
this.updateProgress();
this.setStatus('已停止', 'info');
}
seek(seconds) {
if (!this.isLoaded) return;
const newTime = this.mediaElement.currentTime + seconds;
this.mediaElement.currentTime = Math.max(0, Math.min(newTime, this.mediaElement.duration));
this.logDebug(`跳转 ${seconds} 秒`);
}
seekToPosition(event) {
if (!this.isLoaded) return;
const rect = this.elements.progressBar.getBoundingClientRect();
const clickX = event.clientX - rect.left;
const width = rect.width;
const percentage = clickX / width;
this.mediaElement.currentTime = this.mediaElement.duration * percentage;
this.logDebug(`点击跳转到 ${Math.round(percentage * 100)}%`);
}
setVolume(value) {
const volume = value / 100;
this.mediaElement.volume = volume;
this.elements.volumeValue.textContent = value + '%';
this.logDebug(`音量设置为 ${value}%`);
}
toggleFullscreen() {
if (!this.isVideo || !this.isLoaded) return;
if (!document.fullscreenElement) {
this.mediaElement.requestFullscreen().catch(err => {
console.error('全屏失败:', err);
this.logDebug('全屏失败');
});
} else {
document.exitFullscreen();
}
}
updateProgress() {
if (!this.isLoaded || isNaN(this.mediaElement.duration)) return;
const progress = (this.mediaElement.currentTime / this.mediaElement.duration) * 100;
this.elements.progressFill.style.width = progress + '%';
this.elements.currentTime.textContent = this.formatTime(this.mediaElement.currentTime);
}
updateDuration() {
if (!this.isLoaded || isNaN(this.mediaElement.duration)) return;
this.elements.duration.textContent = this.formatTime(this.mediaElement.duration);
}
updateMediaTypeIndicator() {
if (this.isVideo) {
this.elements.mediaTypeIndicator.textContent = '视频模式';
this.elements.mediaTypeIndicator.className = 'media-type-badge video';
} else {
this.elements.mediaTypeIndicator.textContent = '音频模式';
this.elements.mediaTypeIndicator.className = 'media-type-badge audio';
}
}
resetPlayer() {
this.isLoaded = false;
this.isPlaying = false;
if (this.mediaElement) {
this.mediaElement.pause();
this.mediaElement.src = '';
}
this.elements.progressFill.style.width = '0%';
this.elements.currentTime.textContent = '0:00';
this.elements.duration.textContent = '0:00';
this.elements.mediaContainer.classList.remove('active');
this.elements.loadBtn.disabled = false;
this.elements.playBtn.disabled = true;
this.elements.pauseBtn.disabled = true;
this.elements.stopBtn.disabled = true;
this.elements.rewindBtn.disabled = true;
this.elements.forwardBtn.disabled = true;
this.elements.fullscreenBtn.disabled = true;
this.elements.audioContainer.classList.add('hidden');
this.elements.videoPlaceholder.classList.remove('hidden');
this.elements.videoPlaceholder.textContent = '视频加载中...';
this.elements.mediaTypeIndicator.textContent = '未加载';
this.elements.mediaTypeIndicator.className = 'media-type-badge';
this.logDebug('播放器已重置');
}
formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return mins + ':' + secs.toString().padStart(2, '0');
}
}
// 辅助函数
function setExample(type) {
const examples = {
video: 'https://5blog.cn/content/uploadfile/202602/31641771082987.mp4',
audio: 'https://5blog.cn/content/uploadfile/202602/e7311769955470.mp3'
};
document.getElementById('urlInput').value = examples[type] || '';
// 自动加载
setTimeout(() => {
if (player) {
player.loadMedia();
}
}, 100);
}
// 初始化播放器
const player = new FixedMediaPlayer();
// 页面加载完成后设置视频示例
window.addEventListener('DOMContentLoaded', () => {
console.log('页面加载完成,准备自动加载视频示例');
setTimeout(() => {
setExample('video');
}, 1000);
});
</script>
</body>
</html>
演示效果
阅读:29
发布时间: