[charts] Add Spotify charts!
This commit is contained in:
@ -1,8 +1,9 @@
|
|||||||
from charts.views import ChartRecordView
|
from charts.views import ChartRecordView, SpotifyTracksView
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
app_name = "charts"
|
app_name = "charts"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("charts/", ChartRecordView.as_view(), name="charts-home"),
|
path("charts/", ChartRecordView.as_view(), name="charts-home"),
|
||||||
|
path("charts/spotify/", SpotifyTracksView.as_view(), name="spotify-tracks"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -2,10 +2,11 @@ import calendar
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from charts.models import ChartRecord
|
from charts.models import ChartRecord
|
||||||
from django.db.models import Q
|
from django.db.models import Count, Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from profiles.utils import now_user_timezone
|
from profiles.utils import now_user_timezone
|
||||||
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
MEDIA_TYPE_FILTERS = {
|
MEDIA_TYPE_FILTERS = {
|
||||||
"artist": Q(artist__isnull=False),
|
"artist": Q(artist__isnull=False),
|
||||||
@ -556,3 +557,40 @@ class ChartRecordView(TemplateView):
|
|||||||
return f"/charts/?date={available_years[idx - 1]}"
|
return f"/charts/?date={available_years[idx - 1]}"
|
||||||
return f"/charts/?date={year + 1}"
|
return f"/charts/?date={year + 1}"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class SpotifyTracksView(TemplateView):
|
||||||
|
template_name = "charts/spotify_tracks.html"
|
||||||
|
|
||||||
|
def get_spotify_tracks(self, user, limit=50):
|
||||||
|
track_ids = (
|
||||||
|
Scrobble.objects.filter(
|
||||||
|
user=user,
|
||||||
|
track__isnull=False,
|
||||||
|
)
|
||||||
|
.filter(Q(source="Last.fm") | Q(log__mopidy_source="spotify"))
|
||||||
|
.values("track")
|
||||||
|
.annotate(count=Count("id"))
|
||||||
|
.order_by("-count")[:limit]
|
||||||
|
)
|
||||||
|
from music.models import Track
|
||||||
|
|
||||||
|
track_id_list = [item["track"] for item in track_ids]
|
||||||
|
tracks = Track.objects.filter(id__in=track_id_list)
|
||||||
|
track_map = {t.id: t for t in tracks}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"track": track_map[tid],
|
||||||
|
"count": next(
|
||||||
|
(item["count"] for item in track_ids if item["track"] == tid), 0
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for tid in track_id_list
|
||||||
|
if tid in track_map
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
user = self.request.user
|
||||||
|
context["spotify_tracks"] = self.get_spotify_tracks(user)
|
||||||
|
return context
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="btn-group me-2">
|
<div class="btn-group me-2">
|
||||||
<a href="{% url 'charts:charts-home' %}" class="btn btn-sm btn-outline-secondary">Charts</a>
|
<a href="{% url 'charts:charts-home' %}" class="btn btn-sm btn-outline-secondary">Charts</a>
|
||||||
|
<a href="{% url 'charts:spotify-tracks' %}" class="btn btn-sm btn-outline-secondary">Spotify Tracks</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="btn-group me-2">
|
<div class="btn-group me-2">
|
||||||
|
|||||||
34
vrobbler/templates/charts/spotify_tracks.html
Normal file
34
vrobbler/templates/charts/spotify_tracks.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{% extends "base_list.html" %}
|
||||||
|
|
||||||
|
{% block title %}Spotify Tracks{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<style>
|
||||||
|
.container { margin-bottom: 100px; }
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block lists %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2>🎵 Top Spotify Tracks</h2>
|
||||||
|
<p class="text-muted">Shows tracks scrobbled from Spotify (either via Last.fm or directly from Spotify)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-lg-4 chart-section">
|
||||||
|
<ul class="list-group">
|
||||||
|
{% for item in spotify_tracks %}
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<span class="me-2"><strong>#{{ forloop.counter }}</strong></span>
|
||||||
|
<a href="{{ item.track.get_absolute_url }}">{{ item.track.title }}</a>
|
||||||
|
<span class="badge bg-success rounded-pill">{{ item.count }}</span>
|
||||||
|
</li>
|
||||||
|
{% empty %}
|
||||||
|
<li class="list-group-item">No Spotify tracks found.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user