Spaces:
Running
Running
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>動画プレイヤー</title> | |
<style> | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
background-color: #f0f0f0; | |
font-family: sans-serif; | |
padding: 20px; | |
} | |
video { | |
max-width: 100%; | |
height: auto; | |
margin-bottom: 10px; | |
} | |
.controls { | |
display: flex; | |
flex-direction: column; | |
gap: 15px; | |
width: 100%; | |
max-width: 500px; | |
} | |
.control-group { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
gap: 10px; | |
} | |
input[type="range"] { | |
flex: 1; | |
} | |
input[type="number"] { | |
width: 60px; | |
} | |
#audioWarning { | |
color: red; | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>動画プレイヤー</h1> | |
<video id="videoPlayer" src="v.mp4" controls></video> | |
<p id="audioWarning">音量増幅機能を使用するには、動画を再生してください</p> | |
<div class="controls"> | |
<div class="control-group"> | |
<label for="speedRange">再生速度:</label> | |
<input type="range" id="speedRange" min="0.0001" max="5" step="0.0001" value="1"> | |
<input type="number" id="speedInput" min="0.0001" step="0.0001" value="1"> | |
</div> | |
<div class="control-group"> | |
<label for="volumeRange">音量:</label> | |
<input type="range" id="volumeRange" min="0" max="1000" step="1" value="100"> | |
<input type="number" id="volumeInput" min="0" max="1000" step="1" value="100">% | |
</div> | |
<div class="control-group"> | |
<label for="loopCheckbox">ループ再生:</label> | |
<input type="checkbox" id="loopCheckbox" checked> | |
</div> | |
<button onclick="goFullscreen()">全画面</button> | |
</div> | |
<script> | |
const video = document.getElementById('videoPlayer'); | |
const speedRange = document.getElementById('speedRange'); | |
const speedInput = document.getElementById('speedInput'); | |
const volumeRange = document.getElementById('volumeRange'); | |
const volumeInput = document.getElementById('volumeInput'); | |
const loopCheckbox = document.getElementById('loopCheckbox'); | |
const audioWarning = document.getElementById('audioWarning'); | |
// Web Audio APIの変数 | |
let audioContext = null; | |
let source = null; | |
let gainNode = null; | |
let audioInitialized = false; | |
function setupAudio() { | |
try { | |
audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
source = audioContext.createMediaElementSource(video); | |
gainNode = audioContext.createGain(); | |
source.connect(gainNode); | |
gainNode.connect(audioContext.destination); | |
updateVolume(volumeRange.value); | |
audioInitialized = true; | |
audioWarning.style.display = 'none'; | |
} catch (e) { | |
console.error("AudioContextの初期化に失敗しました:", e); | |
audioWarning.textContent = "音量増幅機能が使用できません: " + e.message; | |
audioWarning.style.display = 'block'; | |
} | |
} | |
function updatePlaybackRate(value) { | |
const speed = parseFloat(value); | |
speedInput.value = speed; | |
speedRange.value = speed; | |
video.playbackRate = speed; | |
} | |
function updateVolume(value) { | |
const volume = parseFloat(value); | |
volumeInput.value = volume; | |
volumeRange.value = volume; | |
if (audioInitialized && gainNode) { | |
gainNode.gain.value = volume / 100; | |
} else { | |
// Web Audio APIが初期化される前は通常の音量制御を使用 | |
video.volume = Math.min(volume / 100, 1); | |
} | |
} | |
// イベントリスナーの設定 | |
['input', 'change', 'mouseup'].forEach(eventName => { | |
speedRange.addEventListener(eventName, () => { | |
updatePlaybackRate(speedRange.value); | |
}); | |
volumeRange.addEventListener(eventName, () => { | |
updateVolume(volumeRange.value); | |
}); | |
}); | |
speedInput.addEventListener('input', () => { | |
updatePlaybackRate(speedInput.value); | |
}); | |
volumeInput.addEventListener('input', () => { | |
updateVolume(volumeInput.value); | |
}); | |
loopCheckbox.addEventListener('change', () => { | |
video.loop = loopCheckbox.checked; | |
}); | |
function goFullscreen() { | |
if (video.requestFullscreen) { | |
video.requestFullscreen(); | |
} else if (video.webkitRequestFullscreen) { | |
video.webkitRequestFullscreen(); | |
} else if (video.msRequestFullscreen) { | |
video.msRequestFullscreen(); | |
} | |
} | |
// 動画再生時にAudioContextを開始(重要!) | |
video.addEventListener('play', () => { | |
if (audioContext.state === 'suspended') { | |
audioContext.resume(); | |
} | |
}); | |
video.addEventListener('loadedmetadata', () => { | |
updatePlaybackRate(speedRange.value); | |
updateVolume(volumeRange.value); | |
video.loop = loopCheckbox.checked; | |
}); | |
// ページ読み込み時に警告を表示 | |
audioWarning.style.display = 'block'; | |
</script> | |
</body> | |
</html> |