Lili Records v1.11

This commit is contained in:
Flatlogic Bot 2026-01-30 14:09:03 +00:00
parent 832958c654
commit ee5b204fea
5 changed files with 70 additions and 8 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-01-30 13:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='station',
name='metadata_api_url',
field=models.URLField(blank=True, help_text='Optional API URL to fetch current track metadata', null=True),
),
]

View File

@ -3,6 +3,7 @@ from django.db import models
class Station(models.Model):
name = models.CharField(max_length=100)
stream_url = models.URLField(help_text="URL for the radio stream")
metadata_api_url = models.URLField(blank=True, null=True, help_text="Optional API URL to fetch current track metadata")
description = models.TextField(blank=True)
color = models.CharField(max_length=7, default="#FF007F", help_text="Hex color for the vinyl")
image = models.FileField(upload_to="stations/", blank=True, null=True)
@ -19,4 +20,4 @@ class Track(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.artist} - {self.title}"
return f"{self.artist} - {self.title}"

View File

@ -34,7 +34,7 @@
<div class="v-bar"></div><div class="v-bar"></div><div class="v-bar"></div><div class="v-bar"></div><div class="v-bar"></div>
<div class="v-bar"></div><div class="v-bar"></div><div class="v-bar"></div><div class="v-bar"></div><div class="v-bar"></div>
</div>
<div class="audio-controls mb-4 p-3 rounded-3 bg-dark bg-opacity-50 border border-secondary">
<audio id="audio-player"></audio>
<div class="d-flex align-items-center gap-3">
@ -53,10 +53,11 @@
<h5 class="mb-3 text-uppercase small ls-wide fw-bold text-secondary">Available Stations</h5>
<div class="list-group list-group-flush bg-transparent custom-scrollbar" style="max-height: 250px; overflow-y: auto;">
{% for station in stations %}
<button class="list-group-item list-group-item-action bg-transparent text-white border-0 py-3 rounded-3 mb-2 station-item"
data-url="{{ station.stream_url }}"
data-name="{{ station.name }}"
data-color="{{ station.color }}">
<button class="list-group-item list-group-item-action bg-transparent text-white border-0 py-3 rounded-3 mb-2 station-item"
data-url="{{ station.stream_url }}"
data-name="{{ station.name }}"
data-color="{{ station.color }}"
data-metadata-url="{{ station.metadata_api_url|default:'' }}">
<div class="d-flex align-items-center">
<div class="station-dot me-3" style="background-color: {{ station.color }}; box-shadow: 0 0 15px {{ station.color }};"></div>
<div class="flex-grow-1">
@ -120,6 +121,11 @@
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
}
#current-track-info {
transition: opacity 0.5s ease-in-out;
font-weight: 500;
text-shadow: 0 0 10px rgba(255, 255, 255, 0.2);
}
</style>
{% endblock %}
@ -134,10 +140,37 @@ document.addEventListener('DOMContentLoaded', function() {
const visualizer = document.getElementById('visualizer');
const stationItems = document.querySelectorAll('.station-item');
const stationNameDisplay = document.getElementById('current-station-name');
const trackInfoDisplay = document.getElementById('current-track-info');
const volumeControl = document.getElementById('volume-control');
const volumeIndicator = document.getElementById('volume-indicator');
let isPlaying = false;
let currentMetadataUrl = '';
let metadataInterval = null;
function fetchMetadata() {
if (!currentMetadataUrl) return;
fetch(currentMetadataUrl)
.then(response => response.json())
.then(data => {
let trackText = 'Virtual Broadcast';
if (data.artist && data.title) {
trackText = `${data.artist} - ${data.title}`;
} else if (data.title) {
trackText = data.title;
}
if (trackInfoDisplay.textContent !== trackText) {
trackInfoDisplay.style.opacity = 0;
setTimeout(() => {
trackInfoDisplay.textContent = trackText;
trackInfoDisplay.style.opacity = 1;
}, 500);
}
})
.catch(err => console.error('Error fetching metadata:', err));
}
function togglePlay() {
if (isPlaying) {
@ -146,6 +179,7 @@ document.addEventListener('DOMContentLoaded', function() {
vinyl.classList.remove('spinning');
tonearm.classList.remove('active');
visualizer.classList.remove('playing');
if (metadataInterval) clearInterval(metadataInterval);
} else {
if (audio.src && audio.src !== window.location.href) {
audio.play().catch(e => {
@ -156,6 +190,10 @@ document.addEventListener('DOMContentLoaded', function() {
vinyl.classList.add('spinning');
tonearm.classList.add('active');
visualizer.classList.add('playing');
// Start fetching metadata
fetchMetadata();
metadataInterval = setInterval(fetchMetadata, 15000); // Every 15 seconds
} else {
alert('Please select a station first!');
return;
@ -171,13 +209,18 @@ document.addEventListener('DOMContentLoaded', function() {
const url = this.dataset.url;
const name = this.dataset.name;
const color = this.dataset.color;
currentMetadataUrl = this.dataset.metadataUrl;
// Update UI
stationItems.forEach(i => i.classList.remove('active'));
this.classList.add('active');
stationNameDisplay.textContent = name;
vinylLabel.style.boxShadow = `0 0 30px ${color}`;
// Reset track info
trackInfoDisplay.textContent = 'Connecting...';
if (metadataInterval) clearInterval(metadataInterval);
// Set source and play
audio.src = url;
isPlaying = false; // Reset state for toggle
@ -191,4 +234,4 @@ document.addEventListener('DOMContentLoaded', function() {
});
});
</script>
{% endblock %}
{% endblock %}