Изменил метод получения ссылки на аудио, чтобы можно было логгировать прослушивание
This commit is contained in:
@@ -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")
|
||||
|
||||
10
music_storage/music/api.py
Normal file
10
music_storage/music/api.py
Normal 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"),
|
||||
]
|
||||
23
music_storage/music/migrations/0013_musiclog.py
Normal file
23
music_storage/music/migrations/0013_musiclog.py
Normal 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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -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}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")),
|
||||
]
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
// Клик по треку
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user