162 lines
6.1 KiB
JavaScript
162 lines
6.1 KiB
JavaScript
const playerButton = document.querySelector('.player-button'),
|
|
audio = document.querySelector('audio'),
|
|
timeline = document.querySelector('.timeline'),
|
|
soundButton = document.querySelector('.sound-button'),
|
|
timeInfo = document.getElementById('timeInfo'),
|
|
playIcon = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
|
|
</svg>
|
|
`,
|
|
pauseIcon = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
|
|
</svg>
|
|
`,
|
|
soundIcon = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
|
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd" />
|
|
</svg>
|
|
`,
|
|
muteIcon = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
|
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
</svg>
|
|
`;
|
|
|
|
function toggleAudio () {
|
|
if (audio.paused) {
|
|
audio.play();
|
|
playerButton.innerHTML = pauseIcon;
|
|
} else {
|
|
audio.pause();
|
|
playerButton.innerHTML = playIcon;
|
|
}
|
|
}
|
|
playerButton.addEventListener('click', toggleAudio);
|
|
|
|
let isSeeking = false;
|
|
|
|
// --- Slider (Timeline) events ---
|
|
// Mouse events
|
|
timeline.addEventListener('mousedown', () => { isSeeking = true; });
|
|
timeline.addEventListener('mouseup', () => {
|
|
isSeeking = false;
|
|
changeSeek(); // Update the audio currentTime based on the slider position
|
|
});
|
|
timeline.addEventListener('change', changeSeek);
|
|
|
|
// Touch events
|
|
timeline.addEventListener('touchstart', () => { isSeeking = true; });
|
|
timeline.addEventListener('touchend', () => {
|
|
isSeeking = false;
|
|
changeSeek();
|
|
});
|
|
timeline.addEventListener('touchcancel', () => { isSeeking = false; });
|
|
|
|
// --- When metadata is loaded, set slider range in seconds ---
|
|
audio.addEventListener('loadedmetadata', () => {
|
|
updateTimeInfo();
|
|
timeline.min = 0;
|
|
timeline.max = audio.duration;
|
|
timeline.value = 0; // Ensures the thumb starts at the left.
|
|
});
|
|
|
|
// --- Update the slider thumb position (in seconds) while playing ---
|
|
function changeTimelinePosition() {
|
|
if (!isSeeking) {
|
|
timeline.value = audio.currentTime;
|
|
const percentagePosition = (audio.currentTime / audio.duration) * 100;
|
|
timeline.style.backgroundSize = `${percentagePosition}% 100%`;
|
|
}
|
|
}
|
|
|
|
// --- Seek function: directly set audio.currentTime using slider's value (in seconds) ---
|
|
function changeSeek() {
|
|
audio.currentTime = timeline.value;
|
|
}
|
|
|
|
// --- Sound toggle ---
|
|
function toggleSound () {
|
|
audio.muted = !audio.muted;
|
|
soundButton.innerHTML = audio.muted ? muteIcon : soundIcon;
|
|
}
|
|
soundButton.addEventListener('click', toggleSound);
|
|
|
|
// --- Utility: Format seconds as mm:ss ---
|
|
function formatTime(seconds) {
|
|
const minutes = Math.floor(seconds / 60);
|
|
const secs = Math.floor(seconds % 60);
|
|
return `${minutes < 10 ? '0' : ''}${minutes}:${secs < 10 ? '0' : ''}${secs}`;
|
|
}
|
|
|
|
// --- Update the time display ---
|
|
function updateTimeInfo() {
|
|
const currentTimeFormatted = formatTime(audio.currentTime);
|
|
const durationFormatted = isNaN(audio.duration) ? "00:00" : formatTime(audio.duration);
|
|
timeInfo.innerHTML = `${currentTimeFormatted} / ${durationFormatted}`;
|
|
}
|
|
|
|
// --- Update timeline, time info, and media session on each time update ---
|
|
audio.ontimeupdate = function() {
|
|
changeTimelinePosition();
|
|
updateTimeInfo();
|
|
if ('mediaSession' in navigator &&
|
|
typeof navigator.mediaSession.setPositionState === 'function' &&
|
|
!isNaN(audio.duration)) {
|
|
navigator.mediaSession.setPositionState({
|
|
duration: audio.duration,
|
|
playbackRate: audio.playbackRate,
|
|
position: audio.currentTime
|
|
});
|
|
}
|
|
};
|
|
|
|
// --- Also update media session on play (in case timeupdate lags) ---
|
|
audio.onplay = function() {
|
|
if ('mediaSession' in navigator &&
|
|
typeof navigator.mediaSession.setPositionState === 'function' &&
|
|
!isNaN(audio.duration)) {
|
|
navigator.mediaSession.setPositionState({
|
|
duration: audio.duration,
|
|
playbackRate: audio.playbackRate,
|
|
position: audio.currentTime
|
|
});
|
|
}
|
|
};
|
|
|
|
// --- Handle external seek requests (e.g. from Android or Windows system controls) ---
|
|
if ('mediaSession' in navigator) {
|
|
navigator.mediaSession.setActionHandler('seekto', function(details) {
|
|
if (details.fastSeek && 'fastSeek' in audio) {
|
|
audio.fastSeek(details.seekTime);
|
|
} else {
|
|
audio.currentTime = details.seekTime;
|
|
}
|
|
changeTimelinePosition();
|
|
});
|
|
}
|
|
|
|
// --- When audio ends ---
|
|
audio.onended = function() {
|
|
playerButton.innerHTML = playIcon;
|
|
};
|
|
|
|
function downloadAudio() {
|
|
// Get the current audio source URL
|
|
const audioSrc = audio.currentSrc || audio.src;
|
|
if (audioSrc) {
|
|
// Create a temporary link element
|
|
const a = document.createElement('a');
|
|
a.href = audioSrc;
|
|
// Extract the file name from the URL, decode it, and set as download attribute
|
|
let fileName = audioSrc.split('/').pop() || 'audio';
|
|
fileName = decodeURIComponent(fileName);
|
|
a.download = fileName;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
}
|
|
}
|
|
|