<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Audio Player</title>
<style>
:root{
--bg:#ffffff; --text:#111; --muted:#6b7280; --line:#e5e7eb;
--chip:#f3f4f6; --chip2:#eef2ff;
--radius:18px;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
.wrap{max-width:860px;margin:22px auto;padding:0 14px}
.panel{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.top{
display:flex;align-items:center;justify-content:space-between;gap:12px;
padding:14px 14px;border-bottom:1px solid var(--line);background:#fafafa;
}
.title{font-weight:600;font-size:14px}
.now{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60vw}
.controls{display:flex;align-items:center;gap:10px}
.btn{
border:1px solid var(--line);background:#fff;border-radius:999px;
width:40px;height:40px;display:grid;place-items:center;cursor:pointer;
user-select:none;
}
.btn:active{transform:translateY(1px)}
.pill{border:1px solid var(--line);background:#fff;border-radius:999px;padding:6px 10px;font-size:12px;color:var(--muted)}
input[type="range"]{width:140px}
.progress{padding:10px 14px;border-bottom:1px solid var(--line);background:#fff}
.seek{width:100%}
.list{display:block;background:#fff}
.row{
display:grid;grid-template-columns: 44px 1fr 90px;
align-items:center;gap:12px;
padding:10px 14px;border-bottom:1px solid var(--line);
}
.row:last-child{border-bottom:0}
.playcell{display:flex;align-items:center;justify-content:center}
.mini{
width:34px;height:34px;border-radius:999px;border:1px solid var(--line);
display:grid;place-items:center;cursor:pointer;background:#fff;
}
.mini:active{transform:translateY(1px)}
.name{font-size:13px;font-weight:600}
.sub{font-size:12px;color:var(--muted)}
.time{font-variant-numeric:tabular-nums;font-size:12px;color:var(--muted);text-align:right}
.active{background:linear-gradient(0deg, var(--chip2), var(--chip2));}
.footer{padding:10px 14px;border-top:1px solid var(--line);background:#fafafa;font-size:12px;color:var(--muted)}
audio{display:none}
</style>
</head>
<body>
<div class="wrap">
<div class="panel">
<div class="top">
<div>
<div class="title">Player</div>
<div class="now" id="now">Listo</div>
</div>
<div class="controls">
<div class="btn" id="prev" title="Anterior">⏮</div>
<div class="btn" id="play" title="Play/Pause">▶️</div>
<div class="btn" id="next" title="Siguiente">⏭</div>
<span class="pill">Vol</span>
<input id="vol" type="range" min="0" max="1" step="0.01" value="1" />
</div>
</div>
<div class="progress">
<input class="seek" id="seek" type="range" min="0" max="1000" value="0" />
<div style="display:flex;justify-content:space-between;margin-top:6px">
<span class="sub" id="tcur">0:00</span>
<span class="sub" id="tdur">0:00</span>
</div>
</div>
<div class="list" id="list"></div>
<div class="footer">Tip: si Canva no deja incrustar, usa el enlace de Cloudflare Pages (pages.dev).</div>
</div>
</div>
<audio id="audio" preload="metadata"></audio>
<script>
// Tus canciones (rutas exactamente como en /audio/)
const tracks = [
{ title: "Basshouse", file: "audio/01-basshouse-mixing.mp3" },
{ title: "DnB", file: "audio/02-dnb-mixing.mp3" },
{ title: "R&B Latin", file: "audio/03-rb-latin-mixing.mp3" },
{ title: "DnB Latin", file: "audio/04-dnb-latin-mix.mp3" },
{ title: "Garage UK", file: "audio/05-garage-uk-mixing.mp3" },
{ title: "Acid House", file: "audio/06-acid-house-mixing.mp3" },
{ title: "Reggaeton", file: "audio/07-reggaeton-mixing.mp3" },
{ title: "House UK", file: "audio/08-house-uk-mixing.mp3" },
{ title: "House UK 2", file: "audio/09-house-uk-2-mixing.mp3" },
{ title: "Synthwave", file: "audio/10-synthwave-mixing.mp3" },
{ title: "UK House", file: "audio/11-uk-house-mixing.mp3" },
{ title: "Lofi 1", file: "audio/12-lofi-1-mixing.mp3" },
{ title: "Lofi", file: "audio/13-lofi-mixing.mp3" },
// ojo: este aún tiene espacios en tu screenshot → renómbralo a 14-lofi-mixing-02.mp3
{ title: "Lofi 2", file: "audio/14-lofi-mixing-02.mp3" },
{ title: "Jazz", file: "audio/15-jazz-mixing.mp3" },
{ title: "Ambient 1", file: "audio/16-ambient-mixing-01.mp3" },
{ title: "Ambient", file: "audio/17-ambient-mixing.mp3" },
];
const audio = document.getElementById("audio");
const list = document.getElementById("list");
const now = document.getElementById("now");
const play = document.getElementById("play");
const prev = document.getElementById("prev");
const next = document.getElementById("next");
const vol = document.getElementById("vol");
const seek = document.getElementById("seek");
const tcur = document.getElementById("tcur");
const tdur = document.getElementById("tdur");
let i = 0;
let seeking = false;
function fmt(s){
if (!isFinite(s)) return "0:00";
s = Math.max(0, Math.floor(s));
const m = Math.floor(s/60);
const r = String(s%60).padStart(2,"0");
return `${m}:${r}`;
}
function render(){
list.innerHTML = "";
tracks.forEach((t, idx) => {
const row = document.createElement("div");
row.className = "row" + (idx===i ? " active" : "");
row.innerHTML = `
<div class="playcell">
<div class="mini" data-idx="${idx}" title="Reproducir">${idx===i && !audio.paused ? "⏸" : "▶️"}</div>
</div>
<div>
<div class="name">${String(idx+1).padStart(2,"0")} · ${t.title}</div>
<div class="sub">${t.file}</div>
</div>
<div class="time" id="time-${idx}">--:--</div>
`;
row.querySelector(".mini").onclick = () => {
if (idx !== i) load(idx, true);
else toggle();
};
list.appendChild(row);
});
}
function setNow(){
now.textContent = `Sonando: ${tracks[i].title}`;
}
function load(idx, autoplay=false){
i = idx;
audio.src = tracks[i].file;
setNow();
render();
if (autoplay) audio.play();
syncPlay();
}
function syncPlay(){
play.textContent = audio.paused ? "▶️" : "⏸";
// actualiza iconos mini sin re-render completo
[...document.querySelectorAll(".mini")].forEach((el) => {
const idx = Number(el.dataset.idx);
el.textContent = (idx===i && !audio.paused) ? "⏸" : "▶️";
});
}
function toggle(){
if (!audio.src) load(i,false);
if (audio.paused) audio.play(); else audio.pause();
syncPlay();
}
play.onclick = toggle;
prev.onclick = () => load((i-1+tracks.length)%tracks.length, true);
next.onclick = () => load((i+1)%tracks.length, true);
vol.oninput = () => audio.volume = Number(vol.value);
audio.addEventListener("loadedmetadata", () => {
tdur.textContent = fmt(audio.duration);
// si quieres, aquí podríamos precargar duraciones por pista (más adelante)
});
audio.addEventListener("timeupdate", () => {
if (!seeking) {
const v = audio.duration ? (audio.currentTime / audio.duration) * 1000 : 0;
seek.value = String(Math.floor(v));
}
tcur.textContent = fmt(audio.currentTime);
tdur.textContent = fmt(audio.duration);
});
seek.addEventListener("input", () => { seeking = true; });
seek.addEventListener("change", () => {
const p = Number(seek.value) / 1000;
if (audio.duration) audio.currentTime = p * audio.duration;
seeking = false;
});
audio.addEventListener("play", syncPlay);
audio.addEventListener("pause", syncPlay);
audio.addEventListener("ended", () => next.click());
// IMPORTANTE: tu pista 14 en la captura aún tiene espacios.
// Renómbrala a: 14-lofi-mixing-02.mp3 para que funcione con este HTML.
render();
load(0, false);
</script>
</body>
</html>