Add Django admin, FeaturedStation model, crmfrsh is superuser
This commit is contained in:
parent
678912020c
commit
3761b13649
7 changed files with 152 additions and 1 deletions
14
accounts/admin.py
Normal file
14
accounts/admin.py
Normal 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
40
radio/admin.py
Normal 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',)
|
||||
29
radio/migrations/0005_featured_station.py
Normal file
29
radio/migrations/0005_featured_station.py
Normal 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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -57,6 +57,22 @@ class FocusSession(models.Model):
|
|||
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):
|
||||
user = models.ForeignKey(
|
||||
User, null=True, blank=True, on_delete=models.SET_NULL, related_name='track_history'
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ from django.views.decorators.http import require_http_methods
|
|||
|
||||
from .icy import stream_icy_metadata
|
||||
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:
|
||||
entry['played_at'] = entry['played_at'].isoformat()
|
||||
|
||||
featured = list(
|
||||
FeaturedStation.objects.filter(active=True).values(
|
||||
'id', 'name', 'url', 'description', 'favicon_url', 'tags'
|
||||
)
|
||||
)
|
||||
|
||||
context = {
|
||||
'saved_stations': saved_stations,
|
||||
'history': history,
|
||||
'amazon_enabled': settings.AMAZON_AFFILIATE_ENABLED,
|
||||
'featured_stations': featured,
|
||||
}
|
||||
return render(request, 'radio/player.html', context)
|
||||
|
||||
|
|
|
|||
|
|
@ -955,3 +955,32 @@ body.dnd-mode .timer-display {
|
|||
from { opacity: 1; transform: translateY(0); }
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,22 @@
|
|||
|
||||
<!-- ===== SAVED TAB ===== -->
|
||||
<section class="tab-panel" id="tab-saved" style="display:none;">
|
||||
{% if featured_stations %}
|
||||
<div class="featured-section">
|
||||
<p class="featured-label">★ 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)">
|
||||
▶ {{ s.name }}
|
||||
</button>
|
||||
{% if s.description %}<span class="muted">{{ s.description }}</span>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
<div id="recommendations" class="recommendations-section">
|
||||
<!-- populated by JS -->
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue