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']
|
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'
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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">★ 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 %}
|
{% if user.is_authenticated %}
|
||||||
<div id="recommendations" class="recommendations-section">
|
<div id="recommendations" class="recommendations-section">
|
||||||
<!-- populated by JS -->
|
<!-- populated by JS -->
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue