Изменил метод получения ссылки на аудио, чтобы можно было логгировать прослушивание

This commit is contained in:
Viner Abubakirov
2026-01-07 12:41:54 +05:00
parent 94afc5dc56
commit ad1267bd43
8 changed files with 103 additions and 19 deletions

View File

@@ -8,6 +8,7 @@ from django.utils.html import format_html
from music.models import Track
from music.models import Album
from music.models import Artist
from music.models import MusicLog
from music.models import Playlist
from music.models import RecommendationPlaylist
@@ -105,3 +106,11 @@ class RecommendationPlaylistAdmin(admin.ModelAdmin):
def make_actual(self, request: HttpRequest, queryset: Any) -> None:
for recommendation in queryset:
recommendation.switch_actual()
@admin.register(MusicLog)
class MusicLogAdmin(admin.ModelAdmin):
list_display = ("track", "played_at")
search_fields = ("track__title", "track__album__artist__name", "track__album__name")
list_filter = ("played_at",)
readonly_fields = ("track", "played_at")

View File

@@ -0,0 +1,10 @@
from django.urls import path
from music.views import TrackAPIView
app_name = "music_api"
urlpatterns = [
path("tracks/<int:pk>/", TrackAPIView.as_view(), name="track_detail"),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 6.0 on 2026-01-07 07:40
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('music', '0012_alter_recommendationplaylist_playlist'),
]
operations = [
migrations.CreateModel(
name='MusicLog',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('played_at', models.DateTimeField(auto_now_add=True)),
('user_ip', models.GenericIPAddressField(blank=True, null=True)),
('track', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='music.track')),
],
),
]

View File

@@ -116,3 +116,12 @@ class RecommendationPlaylist(BaseModel):
.update(is_actual=False))
self.is_actual = True
self.save()
class MusicLog(models.Model):
track = models.ForeignKey(Track, on_delete=models.CASCADE, related_name="logs")
played_at = models.DateTimeField(auto_now_add=True)
user_ip = models.GenericIPAddressField(null=True, blank=True)
def __str__(self):
return f"Played {self.track} at {self.played_at}"

View File

@@ -1,12 +1,14 @@
from django import views as django_views
from django.views.generic import ListView
from django.shortcuts import render
from django.http import JsonResponse
from django.http.request import HttpRequest
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from music.models import Track
from music.models import Artist
from music.models import Album
from music.models import MusicLog
class TrackListView(ListView):
@@ -61,3 +63,24 @@ class AlbumDetailView(django_views.View):
def get(self, request: HttpRequest, pk: int, *args, **kwargs):
album = get_object_or_404(Album, id=pk)
return render(request, "music/album_detail.html", {"album": album})
class TrackAPIView(django_views.View):
def get(self, request: HttpRequest, pk: int, *args, **kwargs):
track = get_object_or_404(Track, id=pk)
data = {
"id": track.id,
"title": track.title,
"url": track.file.url,
"album": {
"id": track.album.id,
"title": track.album.name,
"cover_image": track.album.preview_image.url,
"artist": {
"id": track.album.artist.id,
"name": track.album.artist.name,
},
},
}
MusicLog.objects.create(track=track, user_ip=request.META.get("REMOTE_ADDR", ""))
return JsonResponse(data)

View File

@@ -21,4 +21,6 @@ urlpatterns = [
path('admin/', admin.site.urls),
path('music/', include("music.urls")),
path('', include("core.urls")),
path('api/music/', include("music.api")),
]

View File

@@ -53,12 +53,13 @@ document.addEventListener('DOMContentLoaded', function () {
updateProgressUI(0, audioPlayer.duration);
});
function updateMediaSession(title, artist) {
function updateMediaSession(title, artist, coverImage) {
if (!('mediaSession' in navigator)) return;
navigator.mediaSession.metadata = new MediaMetadata({
title: title,
artist: artist
artist: artist,
artwork: [coverImage ? { src: coverImage, sizes: '512x512', type: 'image/png' } : null].filter(Boolean),
});
navigator.mediaSession.setActionHandler('play', () => audioPlayer.play());
@@ -81,25 +82,32 @@ document.addEventListener('DOMContentLoaded', function () {
currentIndex = index;
const item = trackItems[index];
const src = item.dataset.trackSrc;
const title = item.querySelector('.track-title').textContent;
const artist = item.querySelector('.track-artist').textContent.split(': ')[1];
const api = item.dataset.trackSrc;
fetch(api)
.then(response => response.json())
.then(data => {
const src = data.url;
const title = data.title;
const artist = data.album.artist.name;
const coverImage = data.album.cover_image;
resetActiveTrack();
item.classList.add('active');
updateUI(title, artist);
resetActiveTrack();
item.classList.add('active');
updateUI(title, artist, coverImage);
// ---------------------------
// ВЫЗОВ Media Session API
updateMediaSession(title, artist, coverImage);
// ---------------------------
// ---------------------------
// ВЫЗОВ Media Session API
// ---------------------------
updateMediaSession(title, artist);
// ---------------------------
audioPlayer.src = src;
audioPlayer.title = artist + ' - ' + title;
audioPlayer.play().catch(err => console.log(err));
audioPlayer.src = src;
audioPlayer.title = artist + ' - ' + title;
audioPlayer.play().catch(err => console.log(err));
updatePlayButton(true);
updatePlayButton(true);
})
.catch(err => console.log(err));
}
// Клик по треку

View File

@@ -5,7 +5,7 @@
<ul class="track-list">
{% if tracks %}
{% for track in tracks %}
<li class="track-item" data-track-id="{{ track.id }}" data-track-src="{{ track.file.url }}">
<li class="track-item" data-track-id="{{ track.id }}" data-track-src="{% url 'music_api:track_detail' track.id %}">
<div class="track-info">
<h3 class="track-title">{{ track.title }}</h3>
<p class="track-artist">Исполнитель: {{ track.album.artist }}</p>