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): class Station(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
stream_url = models.URLField(help_text="URL for the radio stream") 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) description = models.TextField(blank=True)
color = models.CharField(max_length=7, default="#FF007F", help_text="Hex color for the vinyl") 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) image = models.FileField(upload_to="stations/", blank=True, null=True)

View File

@ -56,7 +56,8 @@
<button class="list-group-item list-group-item-action bg-transparent text-white border-0 py-3 rounded-3 mb-2 station-item" <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-url="{{ station.stream_url }}"
data-name="{{ station.name }}" data-name="{{ station.name }}"
data-color="{{ station.color }}"> data-color="{{ station.color }}"
data-metadata-url="{{ station.metadata_api_url|default:'' }}">
<div class="d-flex align-items-center"> <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="station-dot me-3" style="background-color: {{ station.color }}; box-shadow: 0 0 15px {{ station.color }};"></div>
<div class="flex-grow-1"> <div class="flex-grow-1">
@ -120,6 +121,11 @@
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border-radius: 10px; 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> </style>
{% endblock %} {% endblock %}
@ -134,10 +140,37 @@ document.addEventListener('DOMContentLoaded', function() {
const visualizer = document.getElementById('visualizer'); const visualizer = document.getElementById('visualizer');
const stationItems = document.querySelectorAll('.station-item'); const stationItems = document.querySelectorAll('.station-item');
const stationNameDisplay = document.getElementById('current-station-name'); const stationNameDisplay = document.getElementById('current-station-name');
const trackInfoDisplay = document.getElementById('current-track-info');
const volumeControl = document.getElementById('volume-control'); const volumeControl = document.getElementById('volume-control');
const volumeIndicator = document.getElementById('volume-indicator'); const volumeIndicator = document.getElementById('volume-indicator');
let isPlaying = false; 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() { function togglePlay() {
if (isPlaying) { if (isPlaying) {
@ -146,6 +179,7 @@ document.addEventListener('DOMContentLoaded', function() {
vinyl.classList.remove('spinning'); vinyl.classList.remove('spinning');
tonearm.classList.remove('active'); tonearm.classList.remove('active');
visualizer.classList.remove('playing'); visualizer.classList.remove('playing');
if (metadataInterval) clearInterval(metadataInterval);
} else { } else {
if (audio.src && audio.src !== window.location.href) { if (audio.src && audio.src !== window.location.href) {
audio.play().catch(e => { audio.play().catch(e => {
@ -156,6 +190,10 @@ document.addEventListener('DOMContentLoaded', function() {
vinyl.classList.add('spinning'); vinyl.classList.add('spinning');
tonearm.classList.add('active'); tonearm.classList.add('active');
visualizer.classList.add('playing'); visualizer.classList.add('playing');
// Start fetching metadata
fetchMetadata();
metadataInterval = setInterval(fetchMetadata, 15000); // Every 15 seconds
} else { } else {
alert('Please select a station first!'); alert('Please select a station first!');
return; return;
@ -171,6 +209,7 @@ document.addEventListener('DOMContentLoaded', function() {
const url = this.dataset.url; const url = this.dataset.url;
const name = this.dataset.name; const name = this.dataset.name;
const color = this.dataset.color; const color = this.dataset.color;
currentMetadataUrl = this.dataset.metadataUrl;
// Update UI // Update UI
stationItems.forEach(i => i.classList.remove('active')); stationItems.forEach(i => i.classList.remove('active'));
@ -178,6 +217,10 @@ document.addEventListener('DOMContentLoaded', function() {
stationNameDisplay.textContent = name; stationNameDisplay.textContent = name;
vinylLabel.style.boxShadow = `0 0 30px ${color}`; vinylLabel.style.boxShadow = `0 0 30px ${color}`;
// Reset track info
trackInfoDisplay.textContent = 'Connecting...';
if (metadataInterval) clearInterval(metadataInterval);
// Set source and play // Set source and play
audio.src = url; audio.src = url;
isPlaying = false; // Reset state for toggle isPlaying = false; // Reset state for toggle