Audius MCP Server
by glassBead-tc
<!DOCTYPE html>
<html>
<head>
<title>Audius Player</title>
<style>
:root {
--primary-color: #7E1BCC;
--secondary-color: #9B4BFF;
--background-color: #121212;
--text-color: #FFFFFF;
--text-secondary: #B3B3B3;
}
body {
margin: 0;
background: var(--background-color);
color: var(--text-color);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
#app {
display: grid;
grid-template-rows: 1fr auto;
height: 100vh;
}
#track-list {
padding: 20px;
overflow-y: auto;
}
.track-item {
display: flex;
align-items: center;
padding: 10px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.track-item:hover {
background: rgba(255, 255, 255, 0.1);
}
.track-item.playing {
background: rgba(126, 27, 204, 0.2);
border-left: 4px solid var(--primary-color);
}
.track-info {
flex-grow: 1;
}
.track-title {
font-weight: 600;
margin-bottom: 4px;
}
.track-artist {
color: var(--text-secondary);
font-size: 0.9em;
}
#player-controls {
background: rgba(0, 0, 0, 0.95);
padding: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.player-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.now-playing {
text-align: center;
}
#current-title {
font-weight: 600;
margin-bottom: 4px;
}
#current-artist {
color: var(--text-secondary);
font-size: 0.9em;
}
.progress-container {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
cursor: pointer;
margin: 10px 0;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
border-radius: 2px;
width: 0%;
transition: width 0.1s linear;
}
.time-info {
display: flex;
justify-content: space-between;
font-size: 0.8em;
color: var(--text-secondary);
}
.controls {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
}
.control-button {
background: none;
border: none;
color: var(--text-color);
cursor: pointer;
padding: 8px;
border-radius: 50%;
transition: background 0.2s;
}
.control-button:hover {
background: rgba(255, 255, 255, 0.1);
}
.volume-control {
display: flex;
align-items: center;
gap: 10px;
}
#volume-slider {
width: 100px;
-webkit-appearance: none;
background: rgba(255, 255, 255, 0.1);
height: 4px;
border-radius: 2px;
}
#volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
background: var(--primary-color);
border-radius: 50%;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<div id="track-list">
<!-- Track list will be populated dynamically -->
</div>
<div id="player-controls">
<div class="now-playing">
<div id="current-title">Select a track</div>
<div id="current-artist"></div>
</div>
<div class="progress-container" id="progress-container">
<div class="progress-bar" id="progress-bar"></div>
</div>
<div class="time-info">
<span id="current-time">0:00</span>
<span id="duration">0:00</span>
</div>
<div class="controls">
<button class="control-button" id="prev-button">⏮</button>
<button class="control-button" id="play-button">▶</button>
<button class="control-button" id="next-button">⏭</button>
<div class="volume-control">
<span id="volume-icon">🔊</span>
<input type="range" id="volume-slider" min="0" max="1" step="0.1" value="1">
</div>
</div>
</div>
<audio id="audio"></audio>
</div>
<script>
class AudioPlayer {
constructor() {
this.audio = document.getElementById('audio');
this.trackList = document.getElementById('track-list');
this.currentTitle = document.getElementById('current-title');
this.currentArtist = document.getElementById('current-artist');
this.progressBar = document.getElementById('progress-bar');
this.progressContainer = document.getElementById('progress-container');
this.currentTime = document.getElementById('current-time');
this.duration = document.getElementById('duration');
this.playButton = document.getElementById('play-button');
this.prevButton = document.getElementById('prev-button');
this.nextButton = document.getElementById('next-button');
this.volumeSlider = document.getElementById('volume-slider');
this.volumeIcon = document.getElementById('volume-icon');
this.tracks = [];
this.currentTrackIndex = -1;
this.setupEventListeners();
this.loadTracks();
}
setupEventListeners() {
// Play/Pause button
this.playButton.addEventListener('click', () => this.togglePlay());
// Previous/Next buttons
this.prevButton.addEventListener('click', () => this.playPrevious());
this.nextButton.addEventListener('click', () => this.playNext());
// Progress bar
this.progressContainer.addEventListener('click', (e) => {
const rect = this.progressContainer.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
this.audio.currentTime = this.audio.duration * percent;
});
// Audio element events
this.audio.addEventListener('timeupdate', () => this.updateProgress());
this.audio.addEventListener('ended', () => this.playNext());
this.audio.addEventListener('play', () => this.playButton.textContent = '⏸');
this.audio.addEventListener('pause', () => this.playButton.textContent = '▶');
// Volume control
this.volumeSlider.addEventListener('input', (e) => {
this.audio.volume = e.target.value;
this.updateVolumeIcon(e.target.value);
});
}
updateVolumeIcon(value) {
if (value >= 0.5) this.volumeIcon.textContent = '🔊';
else if (value > 0) this.volumeIcon.textContent = '🔉';
else this.volumeIcon.textContent = '🔇';
}
async loadTracks() {
try {
// Fetch tracks from your Audius API endpoint
const response = await fetch('/api/tracks');
const tracks = await response.json();
this.tracks = tracks; // Use the URLs directly from the test tracks config
this.renderTrackList();
} catch (error) {
console.error('Error loading tracks:', error);
this.tracks = [];
this.renderTrackList();
}
}
renderTrackList() {
this.trackList.innerHTML = this.tracks.map((track, index) => `
<div class="track-item ${index === this.currentTrackIndex ? 'playing' : ''}"
data-index="${index}">
<div class="track-info">
<div class="track-title">${track.title}</div>
<div class="track-artist">${track.artist}</div>
</div>
</div>
`).join('');
// Add click handlers to track items
document.querySelectorAll('.track-item').forEach(item => {
item.addEventListener('click', () => {
const index = parseInt(item.dataset.index);
this.playTrack(index);
});
});
}
playTrack(index) {
if (index >= 0 && index < this.tracks.length) {
this.currentTrackIndex = index;
const track = this.tracks[index];
// Use the static URL directly
this.audio.src = track.url;
this.audio.play();
this.currentTitle.textContent = track.title;
this.currentArtist.textContent = track.artist;
this.renderTrackList(); // Update playing status
}
}
togglePlay() {
if (this.audio.paused) {
if (this.currentTrackIndex === -1) this.playTrack(0);
else this.audio.play();
} else {
this.audio.pause();
}
}
playPrevious() {
let index = this.currentTrackIndex - 1;
if (index < 0) index = this.tracks.length - 1;
this.playTrack(index);
}
playNext() {
let index = this.currentTrackIndex + 1;
if (index >= this.tracks.length) index = 0;
this.playTrack(index);
}
updateProgress() {
const percent = (this.audio.currentTime / this.audio.duration) * 100;
this.progressBar.style.width = `${percent}%`;
this.currentTime.textContent = this.formatTime(this.audio.currentTime);
this.duration.textContent = this.formatTime(this.audio.duration);
}
formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60).toString().padStart(2, '0');
return `${mins}:${secs}`;
}
}
// Initialize player when the page loads
window.addEventListener('load', () => {
window.player = new AudioPlayer();
});
</script>
</body>
</html>