Add Django admin, FeaturedStation model, crmfrsh is superuser
All checks were successful
Build and push Docker image / build (push) Successful in 12s
Test / test (push) Successful in 13s

This commit is contained in:
marwin 2026-03-16 21:01:51 +01:00
parent 678912020c
commit 3761b13649
7 changed files with 152 additions and 1 deletions

14
accounts/admin.py Normal file
View file

@ -0,0 +1,14 @@
from django.contrib import admin
from .models import UserProfile
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'lastfm_username', 'lastfm_scrobble', 'has_background')
search_fields = ('user__username', 'lastfm_username')
raw_id_fields = ('user',)
def has_background(self, obj):
return bool(obj.background_image_data)
has_background.boolean = True
has_background.short_description = 'Background'

40
radio/admin.py Normal file
View file

@ -0,0 +1,40 @@
from django.contrib import admin
from .models import SavedStation, StationPlay, TrackHistory, FocusSession, FeaturedStation
@admin.register(FeaturedStation)
class FeaturedStationAdmin(admin.ModelAdmin):
list_display = ('name', 'url', 'tags', 'order', 'active')
list_editable = ('order', 'active')
search_fields = ('name', 'tags')
list_filter = ('active',)
@admin.register(SavedStation)
class SavedStationAdmin(admin.ModelAdmin):
list_display = ('name', 'user', 'url', 'is_favorite', 'created_at')
list_filter = ('is_favorite',)
search_fields = ('name', 'user__username', 'url')
raw_id_fields = ('user',)
@admin.register(TrackHistory)
class TrackHistoryAdmin(admin.ModelAdmin):
list_display = ('track', 'station_name', 'user', 'played_at', 'scrobbled')
list_filter = ('scrobbled',)
search_fields = ('track', 'station_name', 'user__username')
raw_id_fields = ('user',)
@admin.register(StationPlay)
class StationPlayAdmin(admin.ModelAdmin):
list_display = ('station_name', 'user', 'started_at', 'ended_at')
search_fields = ('station_name', 'user__username')
raw_id_fields = ('user',)
@admin.register(FocusSession)
class FocusSessionAdmin(admin.ModelAdmin):
list_display = ('user', 'station_name', 'duration_minutes', 'completed_at')
search_fields = ('user__username', 'station_name')
raw_id_fields = ('user',)

View file

@ -0,0 +1,29 @@
# Generated by Django 4.2.29 on 2026-03-16 20:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('radio', '0004_focussession'),
]
operations = [
migrations.CreateModel(
name='FeaturedStation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('url', models.URLField(max_length=500)),
('description', models.CharField(blank=True, max_length=300)),
('favicon_url', models.URLField(blank=True, max_length=500)),
('tags', models.CharField(blank=True, max_length=200)),
('order', models.PositiveIntegerField(default=0)),
('active', models.BooleanField(default=True)),
],
options={
'ordering': ['order', 'name'],
},
),
]

View file

@ -57,6 +57,22 @@ class FocusSession(models.Model):
ordering = ['-completed_at'] ordering = ['-completed_at']
class FeaturedStation(models.Model):
name = models.CharField(max_length=200)
url = models.URLField(max_length=500)
description = models.CharField(max_length=300, blank=True)
favicon_url = models.URLField(max_length=500, blank=True)
tags = models.CharField(max_length=200, blank=True)
order = models.PositiveIntegerField(default=0)
active = models.BooleanField(default=True)
class Meta:
ordering = ['order', 'name']
def __str__(self):
return self.name
class TrackHistory(models.Model): class TrackHistory(models.Model):
user = models.ForeignKey( user = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL, related_name='track_history' User, null=True, blank=True, on_delete=models.SET_NULL, related_name='track_history'

View file

@ -19,7 +19,7 @@ from django.views.decorators.http import require_http_methods
from .icy import stream_icy_metadata from .icy import stream_icy_metadata
from . import lastfm as lastfm_module from . import lastfm as lastfm_module
from .models import SavedStation, StationPlay, TrackHistory, FocusSession from .models import SavedStation, StationPlay, TrackHistory, FocusSession, FeaturedStation
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -54,10 +54,17 @@ def index(request):
for entry in history: for entry in history:
entry['played_at'] = entry['played_at'].isoformat() entry['played_at'] = entry['played_at'].isoformat()
featured = list(
FeaturedStation.objects.filter(active=True).values(
'id', 'name', 'url', 'description', 'favicon_url', 'tags'
)
)
context = { context = {
'saved_stations': saved_stations, 'saved_stations': saved_stations,
'history': history, 'history': history,
'amazon_enabled': settings.AMAZON_AFFILIATE_ENABLED, 'amazon_enabled': settings.AMAZON_AFFILIATE_ENABLED,
'featured_stations': featured,
} }
return render(request, 'radio/player.html', context) return render(request, 'radio/player.html', context)

View file

@ -955,3 +955,32 @@ body.dnd-mode .timer-display {
from { opacity: 1; transform: translateY(0); } from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(12px); } to { opacity: 0; transform: translateY(12px); }
} }
.featured-section {
padding: 8px 0 12px;
border-bottom: 1px solid var(--border, #333);
margin-bottom: 12px;
}
.featured-label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--muted, #888);
margin: 0 0 8px;
}
.featured-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
.featured-list li {
display: flex;
align-items: center;
gap: 8px;
}

View file

@ -74,6 +74,22 @@
<!-- ===== SAVED TAB ===== --> <!-- ===== SAVED TAB ===== -->
<section class="tab-panel" id="tab-saved" style="display:none;"> <section class="tab-panel" id="tab-saved" style="display:none;">
{% if featured_stations %}
<div class="featured-section">
<p class="featured-label">&#9733; Featured</p>
<ul class="featured-list">
{% for s in featured_stations %}
<li>
{% if s.favicon_url %}<img src="{{ s.favicon_url }}" class="station-favicon" alt="">{% endif %}
<button class="btn btn-sm" onclick="playStation('{{ s.url|escapejs }}', '{{ s.name|escapejs }}', null)">
&#9654; {{ s.name }}
</button>
{% if s.description %}<span class="muted">{{ s.description }}</span>{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<div id="recommendations" class="recommendations-section"> <div id="recommendations" class="recommendations-section">
<!-- populated by JS --> <!-- populated by JS -->