'"clone" audio player to control another?
I have a list of players that user scrolls through and I'm trying create an always-visible player control, so user don't have to scroll through the list looking for the player that is playing sound.
I'd like somehow "clone" <audio> element to make it not actually play anything, just show information about currently playing media in another player and visually be identical to original player.
Currently I generate my own "player" and use media events to gather/display information about playing player, but it probably looks different in different browsers/systems.
Any suggestions?
const content = document.getElementById("content");
const eventHandler = (e =>
{
const remote = document.getElementById("remote"), //our custom "player"
progress = remote.querySelector(".progress"),
time = remote.querySelector(".time"),
position = time.querySelector(".position"),
duration = time.querySelector(".duration");
;
let player = null; //this will hold current player
//list of event handlers
const handler = {
play: e =>
{
if (player && player !== e.target)
player.pause();
player = e.target;
remote.classList.add("player");
remote.classList.add("play");
content.querySelector(".selected")?.classList.remove("selected");
player.classList.add("selected");
handler.timeupdate(e);
handler.durationchange(e);
},
pause: e =>
{
if (player.paused)
remote.classList.remove("play");
},
timeupdate: e =>
{
if (player !== e.target)
return;
position.textContent = readableTime(e.target.currentTime);
progress.value = e.target.currentTime;
},
durationchange: e =>
{
if (player !== e.target)
return;
duration.textContent = readableTime(e.target.duration);
progress.max = e.target.duration;
}
}
const readableTime = (t, ms) =>
{
const seconds = ~~t,
minutes = ~~(seconds / 60);
return ("" + minutes).padStart(1, "0") + ":" +
("" + seconds % 60).padStart(2, "0") +
(ms ? "." + (t.toFixed(1)+".0").substr((""+t).indexOf(".")+1, 1) : "");
}
remote.querySelector(".playpause").addEventListener("click", e =>
{
player[player.paused ? "play" : "pause"]();
});
progress.addEventListener("input", e =>
{
player.currentTime = progress.value;
});
return e =>
{
try{handler[e.type](e)}catch(er){console.error(er)};
};
})();
document.addEventListener("play", eventHandler, true);
document.addEventListener("pause", eventHandler, true);
document.addEventListener("timeupdate", eventHandler, true);
/* show players list */
{
const files = ["Zh[Wci", "ceei[", "iY_\\_", "ZWdY[", "^eki[", "fefZWdY["];
let file,
player = document.createElement("audio");
player.setAttribute("controls", "");
player.setAttribute("nodownload", "");
while((file = files.splice(~~(Math.random() * files.length), 1)).length)
{
/* https://www.bensound.com/ */
player.src = `^jjfi0%%mmm$X[diekdZ$Yec%X[diekdZ#cki_Y%X[diekdZ#${file[0]}$cf)`.replace(/./g,a=>String.fromCharCode(a.charCodeAt(0)+10) ) ;
content.appendChild(player);
player = player.cloneNode(true);
}
}
.content
{
display: flex;
margin: auto;
}
#content
{
display: grid;
max-height: 7em;
overflow-y: auto;
margin-top: 1em;
}
#remote
{
font-family: Roboto-Regular, Roboto, sans-serif;
font-size: 14px;
display: flex;
user-select: none;
cursor: default;
border: 1px solid black;
background-color: white;
color: black;
border-radius: 2em;
width: fit-content;
height: 2em;
padding: 0.5em 1.3em;
align-items: center;
gap: 10px;
max-height: 2em;
}
#remote:not(.player)
{
pointer-events: none;
opacity: 0.5;
}
/* play/pause button */
#remote .playpause
{
height: 1em;
width: 1em;
padding: 0.5em;
margin: auto -0.3em;
border-radius: 100%;
text-align: center;
cursor: pointer;
}
/* play */
#remote .playpause:after
{
--size: 1em;
width: var(--size);
height: var(--size);
content: "";
display: inline-block;
box-sizing: border-box;
border-style: solid;
border-width: calc(var(--size) / 2) 0 calc(var(--size) / 2) var(--size);
border-color: transparent transparent transparent black;
transition: all 100ms ease-in-out;
}
/* pause */
#remote.play .playpause:after
{
border-style: double;
border-width: 0 0 0 var(--size);
}
#remote .playpause:hover
{
background-color: rgba(32, 33, 36, 0.06);
}
#remote .progress
{
cursor: pointer;
}
#remote span
{
}
audio
{
border: 2px solid transparent;
border-radius: 2em;
height: 2.5em;
}
audio.selected
{
outline: 0;
border-color: orange;
}
<div id="remote">
<span class="playpause"></span>
<span class="time">
<span class="position">0:00</span>
/
<span class="duration">0:00</span>
</span>
<input type="range" class="progress" min="0" value="0">
</div>
<div class="content">
<div id="content">
</div>
</div>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
