Add Music section: hub, template, build pipeline, and homepage wire
Music/build.py parses *-music.md files and generates collection pages supporting YouTube video embeds (lazy IntersectionObserver) and FileBrowser audio cards with floating media player (prev/next/auto-advance). Music star node wired in index.html. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
705
Music/oldmusicandmvs.html
Normal file
705
Music/oldmusicandmvs.html
Normal file
@@ -0,0 +1,705 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Old Music and Music Videos - The Archive | MUSIC</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Rancho&family=Rambla:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-void: #04060b;
|
||||
--text-warm: #e8d5b8;
|
||||
--mu-red: #8b2020;
|
||||
--mu-orange: #ff7f3f;
|
||||
--mu-red-glow: rgba(139, 32, 32, 0.25);
|
||||
--mu-orange-glow: rgba(255, 127, 63, 0.25);
|
||||
--transition: 100ms ease;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; border-radius: 0; }
|
||||
|
||||
body {
|
||||
background-color: var(--bg-void);
|
||||
color: var(--text-warm);
|
||||
font-family: 'Rambla', sans-serif;
|
||||
line-height: 1.6;
|
||||
padding-top: 64px;
|
||||
}
|
||||
|
||||
body.player-active { padding-bottom: 64px; }
|
||||
|
||||
/* --- Sticky header --- */
|
||||
.sticky-header {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg-void);
|
||||
border-bottom: 2px solid var(--mu-red);
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-family: 'Rancho', cursive;
|
||||
font-size: 1.5rem;
|
||||
color: var(--mu-orange);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.header-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
color: var(--mu-orange);
|
||||
text-decoration: none;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.nav-link:hover { color: #ff9f6f; }
|
||||
|
||||
.count {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.1em;
|
||||
color: rgba(232, 213, 184, 0.5);
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--mu-orange);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.menu-toggle { display: block; }
|
||||
.header-nav {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%; right: 0;
|
||||
background: var(--bg-void);
|
||||
border: 2px solid var(--mu-red);
|
||||
border-top: none;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
width: 200px;
|
||||
}
|
||||
.header-nav.open { display: flex; }
|
||||
}
|
||||
|
||||
/* --- Lightbox --- */
|
||||
#lightbox {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(4, 6, 11, 0.9);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
padding: 2rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lightbox-content {
|
||||
background: #0c0a08;
|
||||
border: 2px solid var(--mu-red);
|
||||
border-top: 8px solid var(--mu-orange);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding: 2.5rem;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
}
|
||||
.lightbox-close {
|
||||
position: absolute;
|
||||
top: 0.5rem; right: 0.75rem;
|
||||
font-size: 2rem;
|
||||
color: var(--mu-orange);
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
.lightbox-quote {
|
||||
font-style: italic;
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-warm);
|
||||
margin-bottom: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
.lightbox-attribution {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
color: var(--mu-orange);
|
||||
text-align: right;
|
||||
}
|
||||
.lightbox-hint {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.1em;
|
||||
color: rgba(232, 213, 184, 0.4);
|
||||
}
|
||||
|
||||
/* --- Audio Card --- */
|
||||
.audio-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.audio-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.play-btn {
|
||||
background: none;
|
||||
border: 2px solid var(--mu-orange);
|
||||
color: var(--mu-orange);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
font-family: inherit;
|
||||
}
|
||||
.play-btn:hover {
|
||||
background: var(--mu-orange);
|
||||
color: var(--bg-void);
|
||||
}
|
||||
.audio-info { flex-grow: 1; }
|
||||
.download-link {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
color: var(--mu-orange);
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
transition: var(--transition);
|
||||
}
|
||||
.download-link:hover { text-decoration: underline; }
|
||||
.progress-container {
|
||||
height: 4px;
|
||||
background: var(--mu-red);
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: var(--mu-orange);
|
||||
width: 0%;
|
||||
transition: width 100ms linear;
|
||||
}
|
||||
.duration-meta {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.7rem;
|
||||
color: rgba(232, 213, 184, 0.5);
|
||||
margin-top: 0.25rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* --- Floating Player --- */
|
||||
.floating-player {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: var(--bg-void);
|
||||
border-top: 2px solid var(--mu-red);
|
||||
z-index: 500;
|
||||
display: none;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
.floating-player.active { display: flex; }
|
||||
.fp-info { flex: 1; min-width: 0; }
|
||||
.fp-title {
|
||||
font-family: 'Rancho', cursive;
|
||||
font-size: 1.2rem;
|
||||
color: var(--mu-orange);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.fp-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
.fp-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--mu-orange);
|
||||
font-family: 'Rambla', sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
.fp-btn:hover { color: var(--text-warm); }
|
||||
.fp-progress-wrap {
|
||||
position: absolute;
|
||||
top: -2px; left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--mu-red);
|
||||
}
|
||||
.fp-progress-fill {
|
||||
height: 100%;
|
||||
background: var(--mu-orange);
|
||||
width: 0%;
|
||||
transition: width 100ms linear;
|
||||
}
|
||||
|
||||
/* --- Cards --- */
|
||||
@keyframes cardIn {
|
||||
from { opacity: 0; transform: translateY(16px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 900px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1rem 4rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: 'Rancho', cursive;
|
||||
font-size: 3rem;
|
||||
color: var(--mu-orange);
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 3rem;
|
||||
border: 2px solid var(--mu-red);
|
||||
background: rgba(139, 32, 32, 0.05);
|
||||
transition: border-color var(--transition), box-shadow var(--transition);
|
||||
animation: cardIn 320ms ease both;
|
||||
}
|
||||
|
||||
.card:nth-child(1) { animation-delay: 0ms; }
|
||||
.card:nth-child(2) { animation-delay: 50ms; }
|
||||
.card:nth-child(3) { animation-delay: 100ms; }
|
||||
.card:nth-child(4) { animation-delay: 150ms; }
|
||||
.card:nth-child(5) { animation-delay: 200ms; }
|
||||
.card:nth-child(6) { animation-delay: 250ms; }
|
||||
.card:nth-child(7) { animation-delay: 300ms; }
|
||||
.card:nth-child(8) { animation-delay: 350ms; }
|
||||
.card:nth-child(9) { animation-delay: 400ms; }
|
||||
.card:nth-child(10) { animation-delay: 450ms; }
|
||||
.card:nth-child(11) { animation-delay: 500ms; }
|
||||
.card:nth-child(12) { animation-delay: 550ms; }
|
||||
|
||||
.card:hover {
|
||||
border-color: var(--mu-orange);
|
||||
box-shadow: 0 0 8px 2px var(--mu-orange-glow);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid var(--mu-red);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-family: 'Rancho', cursive;
|
||||
font-size: 1.6rem;
|
||||
color: var(--mu-orange);
|
||||
}
|
||||
|
||||
.video-container {
|
||||
position: relative;
|
||||
padding-bottom: 56.25%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.video-container iframe {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="lightbox">
|
||||
<div class="lightbox-content">
|
||||
<span class="lightbox-close">×</span>
|
||||
<p class="lightbox-quote">I much prefer making music live. But here are some old recordings. Oh and some music videos that I made for better recording artists than me at the time. At time of writing, there are inklings of thinkings about returning to making music. I'm considering it. We'll see how it plays out.</p>
|
||||
<p class="lightbox-attribution">— JL</p>
|
||||
<span class="lightbox-hint">CLICK ANYWHERE TO DISMISS</span>
|
||||
</div>
|
||||
</div>
|
||||
<header class="sticky-header">
|
||||
<div class="header-title">OLD MUSIC AND MUSIC VIDEOS - THE ARCHIVE</div>
|
||||
<button class="menu-toggle">☰</button>
|
||||
<nav class="header-nav">
|
||||
<a href="music-hub.html" class="nav-link">← MUSIC</a>
|
||||
<span class="count">14 TRACKS</span>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h1 class="section-title">OLD MUSIC AND MUSIC VIDEOS - THE ARCHIVE</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">Sixteen by Melanie Kerr</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/zmwv8NXMO6E"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">Good Girl by Melanie Kerr</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/M4KCZr7Xx7A"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">042 Letter to My Sunrise \(Demo Tape\)</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/lbhFd6I_CII"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">038 Trouble Done Bore Me Down</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/fpm0nfMjm7M"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">036 That's All Right Mama</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/N_8WL1GhW0s"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">035 Psycho Bch</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/fsE03yW7M9Y"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">034 On the Curious Creatures Called People</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/JJtnygYcs5o"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">033 Ode to Truth Kind of</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/QVQl2uyUBGg"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">032 iYeza</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/1rN4C828vR4"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">031 Dammit Blink 182 Cover</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/YTZqMbIuGKk"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">030 An Irish Airman Forsees his Death</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/wOe-KTlWX1Y"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">Trouble Done Bore Me Down</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/BWJrOOYqRuM"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">Shortstraw</h2></div>
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
data-src="https://www.youtube.com/embed/pIGIW3h9LpU"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" id="track-1">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Dammit - Dwonlaod \ - Free On File Browser</h2>
|
||||
</div>
|
||||
<div class="audio-card">
|
||||
<div class="audio-controls">
|
||||
<button class="play-btn"
|
||||
data-src="https://files.exopraxist.org/api/public/dl/KeQTyi9r"
|
||||
data-title="Dammit - Dwonlaod \ - Free On File Browser">▶</button>
|
||||
<div class="audio-info">
|
||||
<a href="https://files.exopraxist.org/share/KeQTyi9r" target="_blank" rel="noopener"
|
||||
class="download-link">⬇ DOWNLOAD</a>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<div class="duration-meta">
|
||||
<span class="current-time">0:00</span>
|
||||
<span class="total-time">--:--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="floating-player">
|
||||
<div class="fp-progress-wrap"><div class="fp-progress-fill"></div></div>
|
||||
<div class="fp-info"><div class="fp-title">No Track Selected</div></div>
|
||||
<div class="fp-controls">
|
||||
<button class="fp-btn" id="fp-prev">◀◀</button>
|
||||
<button class="fp-btn" id="fp-play">▶</button>
|
||||
<button class="fp-btn" id="fp-next">▶▶</button>
|
||||
</div>
|
||||
</div>
|
||||
<audio id="main-audio"></audio>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const lightbox = document.getElementById('lightbox');
|
||||
if (!localStorage.getItem('music-oldmusicandmvs-seen')) {
|
||||
lightbox.style.display = 'flex';
|
||||
localStorage.setItem('music-oldmusicandmvs-seen', '1');
|
||||
}
|
||||
const closeLightbox = () => { lightbox.style.display = 'none'; };
|
||||
lightbox.addEventListener('click', (e) => {
|
||||
if (e.target === lightbox) closeLightbox();
|
||||
});
|
||||
document.querySelector('.lightbox-close').addEventListener('click', closeLightbox);
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') closeLightbox();
|
||||
});
|
||||
|
||||
// --- Mobile nav ---
|
||||
const menuToggle = document.querySelector('.menu-toggle');
|
||||
const nav = document.querySelector('.header-nav');
|
||||
menuToggle.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
nav.classList.toggle('open');
|
||||
});
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!nav.contains(e.target) && e.target !== menuToggle)
|
||||
nav.classList.remove('open');
|
||||
});
|
||||
|
||||
// --- Lazy-load iframes ---
|
||||
const observer = new IntersectionObserver((entries, obs) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const iframe = entry.target;
|
||||
if (iframe.dataset.src) {
|
||||
iframe.src = iframe.dataset.src;
|
||||
iframe.removeAttribute('data-src');
|
||||
obs.unobserve(iframe);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '200px' });
|
||||
document.querySelectorAll('iframe[data-src]').forEach(f => observer.observe(f));
|
||||
|
||||
// --- Audio Logic ---
|
||||
const audio = document.getElementById('main-audio');
|
||||
const floatingPlayer = document.querySelector('.floating-player');
|
||||
const fpPlayBtn = document.getElementById('fp-play');
|
||||
const fpTitle = document.querySelector('.fp-title');
|
||||
const fpFill = document.querySelector('.fp-progress-fill');
|
||||
let currentTrackBtn = null;
|
||||
|
||||
const formatTime = (t) => {
|
||||
const m = Math.floor(t / 60);
|
||||
const s = Math.floor(t % 60);
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const updateUI = () => {
|
||||
const playing = !audio.paused;
|
||||
const icon = playing ? '▐▐' : '▶';
|
||||
if (currentTrackBtn) currentTrackBtn.textContent = icon;
|
||||
fpPlayBtn.textContent = icon;
|
||||
if (playing) {
|
||||
floatingPlayer.classList.add('active');
|
||||
document.body.classList.add('player-active');
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll('.play-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const src = btn.dataset.src;
|
||||
const title = btn.dataset.title;
|
||||
if (audio.getAttribute('data-loaded') === src) {
|
||||
if (audio.paused) audio.play(); else audio.pause();
|
||||
} else {
|
||||
if (currentTrackBtn) currentTrackBtn.textContent = '▶';
|
||||
audio.src = src;
|
||||
audio.setAttribute('data-loaded', src);
|
||||
fpTitle.textContent = title;
|
||||
currentTrackBtn = btn;
|
||||
audio.play();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
fpPlayBtn.addEventListener('click', () => {
|
||||
if (audio.paused) audio.play(); else audio.pause();
|
||||
});
|
||||
|
||||
audio.addEventListener('play', updateUI);
|
||||
audio.addEventListener('pause', updateUI);
|
||||
|
||||
audio.addEventListener('timeupdate', () => {
|
||||
if (!audio.duration) return;
|
||||
const pct = (audio.currentTime / audio.duration) * 100;
|
||||
if (fpFill) fpFill.style.width = pct + '%';
|
||||
if (currentTrackBtn) {
|
||||
const card = currentTrackBtn.closest('.audio-card');
|
||||
const bar = card.querySelector('.progress-bar');
|
||||
const cur = card.querySelector('.current-time');
|
||||
if (bar) bar.style.width = pct + '%';
|
||||
if (cur) cur.textContent = formatTime(audio.currentTime);
|
||||
}
|
||||
});
|
||||
|
||||
audio.addEventListener('loadedmetadata', () => {
|
||||
if (currentTrackBtn) {
|
||||
const card = currentTrackBtn.closest('.audio-card');
|
||||
const tot = card.querySelector('.total-time');
|
||||
if (tot) tot.textContent = formatTime(audio.duration);
|
||||
}
|
||||
});
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
const allBtns = [...document.querySelectorAll('.play-btn')];
|
||||
const idx = allBtns.indexOf(currentTrackBtn);
|
||||
const next = allBtns[idx + 1];
|
||||
if (next) {
|
||||
next.click();
|
||||
} else {
|
||||
audio.pause();
|
||||
if (currentTrackBtn) currentTrackBtn.textContent = '▶';
|
||||
fpPlayBtn.textContent = '▶';
|
||||
floatingPlayer.classList.remove('active');
|
||||
document.body.classList.remove('player-active');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('fp-prev').addEventListener('click', () => {
|
||||
const all = [...document.querySelectorAll('.play-btn')];
|
||||
const prev = all[all.indexOf(currentTrackBtn) - 1];
|
||||
if (prev) prev.click();
|
||||
});
|
||||
|
||||
document.getElementById('fp-next').addEventListener('click', () => {
|
||||
const all = [...document.querySelectorAll('.play-btn')];
|
||||
const next = all[all.indexOf(currentTrackBtn) + 1];
|
||||
if (next) next.click();
|
||||
});
|
||||
|
||||
document.querySelectorAll('.progress-container').forEach(bar => {
|
||||
bar.addEventListener('click', (e) => {
|
||||
const rect = bar.getBoundingClientRect();
|
||||
audio.currentTime = ((e.clientX - rect.left) / rect.width) * audio.duration;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user