mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-22 00:26:36 +03:00
added anon user listen history
This commit is contained in:
parent
f6467bd12a
commit
6f70c38ecf
|
@ -5,6 +5,7 @@
|
|||
from akarpov.common.api.serializers import SetUserModelSerializer
|
||||
from akarpov.music.models import (
|
||||
Album,
|
||||
AnonMusicUser,
|
||||
Author,
|
||||
Playlist,
|
||||
PlaylistSong,
|
||||
|
@ -198,6 +199,26 @@ def create(self, validated_data):
|
|||
return playlist_song
|
||||
|
||||
|
||||
class ListenSongSerializer(serializers.Serializer):
|
||||
song = serializers.SlugField()
|
||||
user_id = serializers.CharField(required=False)
|
||||
|
||||
def validate(self, attrs):
|
||||
if not Song.objects.filter(slug=attrs["song"]).exists():
|
||||
raise serializers.ValidationError("Song not found")
|
||||
|
||||
if "user_id" in attrs and attrs["user_id"]:
|
||||
if not AnonMusicUser.objects.filter(id=attrs["user_id"]).exists():
|
||||
raise serializers.ValidationError("User not found")
|
||||
return attrs
|
||||
|
||||
|
||||
class AnonMusicUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AnonMusicUser
|
||||
fields = ["id"]
|
||||
|
||||
|
||||
class LikeDislikeSongSerializer(serializers.ModelSerializer):
|
||||
song = serializers.SlugField()
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from akarpov.music.api.views import (
|
||||
AddSongToPlaylistAPIView,
|
||||
CreateAnonMusicUserAPIView,
|
||||
DislikeSongAPIView,
|
||||
LikeSongAPIView,
|
||||
ListAlbumsAPIView,
|
||||
|
@ -76,4 +77,5 @@
|
|||
RetrieveUpdateDestroyAuthorAPIView.as_view(),
|
||||
name="retrieve_update_delete_author",
|
||||
),
|
||||
path("anon/create/", CreateAnonMusicUserAPIView.as_view(), name="create-anon"),
|
||||
]
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
from akarpov.common.api.permissions import IsAdminOrReadOnly, IsCreatorOrReadOnly
|
||||
from akarpov.music.api.serializers import (
|
||||
AddSongToPlaylistSerializer,
|
||||
AnonMusicUserSerializer,
|
||||
FullAlbumSerializer,
|
||||
FullAuthorSerializer,
|
||||
FullPlaylistSerializer,
|
||||
LikeDislikeSongSerializer,
|
||||
ListAlbumSerializer,
|
||||
ListAuthorSerializer,
|
||||
ListenSongSerializer,
|
||||
ListPlaylistSerializer,
|
||||
ListSongSerializer,
|
||||
PlaylistSerializer,
|
||||
|
@ -314,7 +316,7 @@ class RetrieveUpdateDestroyAuthorAPIView(
|
|||
|
||||
|
||||
class ListenSongAPIView(generics.GenericAPIView):
|
||||
serializer_class = LikeDislikeSongSerializer
|
||||
serializer_class = ListenSongSerializer
|
||||
permission_classes = [permissions.AllowAny]
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -331,12 +333,25 @@ def post(self, request, *args, **kwargs):
|
|||
return Response(status=404)
|
||||
if self.request.user.is_authenticated:
|
||||
listen_to_song.apply_async(
|
||||
kwargs={"song_id": song.id, "user_id": self.request.user.id},
|
||||
kwargs={
|
||||
"song_id": song.id,
|
||||
"user_id": self.request.user.id,
|
||||
"anon": False,
|
||||
},
|
||||
countdown=2,
|
||||
)
|
||||
elif "user_id" in data:
|
||||
listen_to_song.apply_async(
|
||||
kwargs={
|
||||
"song_id": song.id,
|
||||
"user_id": data["user_id"],
|
||||
"anon": True,
|
||||
},
|
||||
countdown=2,
|
||||
)
|
||||
else:
|
||||
listen_to_song.apply_async(
|
||||
kwargs={"song_id": song.id},
|
||||
kwargs={"song_id": song.id, "user_id": None, "anon": True},
|
||||
countdown=2,
|
||||
)
|
||||
return Response(status=201)
|
||||
|
@ -353,3 +368,8 @@ def get_queryset(self):
|
|||
.filter(user=self.request.user)
|
||||
.values_list("song_id", flat=True)
|
||||
)
|
||||
|
||||
|
||||
class CreateAnonMusicUserAPIView(generics.CreateAPIView):
|
||||
serializer_class = AnonMusicUserSerializer
|
||||
permission_classes = [permissions.AllowAny]
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import uuid
|
||||
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
|
@ -154,6 +156,29 @@ class Meta:
|
|||
ordering = ["-created"]
|
||||
|
||||
|
||||
class AnonMusicUser(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
|
||||
def __str__(self):
|
||||
return f"AnonMusicUser {self.id}"
|
||||
|
||||
|
||||
class AnonMusicUserHistory(models.Model):
|
||||
user = models.ForeignKey(
|
||||
"music.AnonMusicUser", related_name="songs_listened", on_delete=models.CASCADE
|
||||
)
|
||||
song = models.ForeignKey(
|
||||
"Song", related_name="anon_listeners", on_delete=models.CASCADE
|
||||
)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user} - {self.song}"
|
||||
|
||||
|
||||
class UserListenHistory(models.Model):
|
||||
user = models.ForeignKey(
|
||||
"users.User", related_name="songs_listened", on_delete=models.CASCADE
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from datetime import timedelta
|
||||
|
||||
import pylast
|
||||
import structlog
|
||||
from asgiref.sync import async_to_sync
|
||||
|
@ -9,7 +11,14 @@
|
|||
from pytube import Channel, Playlist
|
||||
|
||||
from akarpov.music.api.serializers import SongSerializer
|
||||
from akarpov.music.models import RadioSong, Song, UserListenHistory, UserMusicProfile
|
||||
from akarpov.music.models import (
|
||||
AnonMusicUser,
|
||||
AnonMusicUserHistory,
|
||||
RadioSong,
|
||||
Song,
|
||||
UserListenHistory,
|
||||
UserMusicProfile,
|
||||
)
|
||||
from akarpov.music.services import yandex, youtube
|
||||
from akarpov.music.services.file import load_dir, load_file
|
||||
from akarpov.utils.celery import get_scheduled_tasks_name
|
||||
|
@ -96,7 +105,7 @@ def start_next_song(previous_ids: list):
|
|||
|
||||
|
||||
@shared_task
|
||||
def listen_to_song(song_id, user_id=None):
|
||||
def listen_to_song(song_id, user_id=None, anon=True):
|
||||
# protection from multiple listen,
|
||||
# check that last listen by user was more than the length of the song
|
||||
# and last listened song is not the same
|
||||
|
@ -104,38 +113,63 @@ def listen_to_song(song_id, user_id=None):
|
|||
s.played += 1
|
||||
s.save(update_fields=["played"])
|
||||
if user_id:
|
||||
try:
|
||||
last_listen = UserListenHistory.objects.filter(user_id=user_id).latest("id")
|
||||
except UserListenHistory.DoesNotExist:
|
||||
last_listen = None
|
||||
if (
|
||||
last_listen
|
||||
and last_listen.song_id == song_id
|
||||
or last_listen
|
||||
and last_listen.created + s.length > now()
|
||||
):
|
||||
return
|
||||
UserListenHistory.objects.create(
|
||||
user_id=user_id,
|
||||
song_id=song_id,
|
||||
)
|
||||
try:
|
||||
user_profile = UserMusicProfile.objects.get(user_id=user_id)
|
||||
lastfm_token = user_profile.lastfm_token
|
||||
|
||||
# Initialize Last.fm network with the user's session key
|
||||
network = pylast.LastFMNetwork(
|
||||
api_key=settings.LAST_FM_API_KEY,
|
||||
api_secret=settings.LAST_FM_SECRET,
|
||||
session_key=lastfm_token,
|
||||
if anon:
|
||||
try:
|
||||
anon_user = AnonMusicUser.objects.get(id=user_id)
|
||||
except AnonMusicUser.DoesNotExist:
|
||||
anon_user = AnonMusicUser.objects.create(id=user_id)
|
||||
try:
|
||||
last_listen = AnonMusicUserHistory.objects.filter(
|
||||
user_id=user_id
|
||||
).last()
|
||||
except AnonMusicUserHistory.DoesNotExist:
|
||||
last_listen = None
|
||||
if (
|
||||
last_listen
|
||||
and last_listen.song_id == song_id
|
||||
or last_listen
|
||||
and last_listen.created + timedelta(seconds=s.length) > now()
|
||||
):
|
||||
return
|
||||
AnonMusicUserHistory.objects.create(
|
||||
user=anon_user,
|
||||
song_id=song_id,
|
||||
)
|
||||
song = Song.objects.get(id=song_id)
|
||||
artist_name = song.artists_names
|
||||
track_name = song.name
|
||||
timestamp = int(timezone.now().timestamp())
|
||||
network.scrobble(artist=artist_name, title=track_name, timestamp=timestamp)
|
||||
except UserMusicProfile.DoesNotExist:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Last.fm scrobble error: {e}")
|
||||
else:
|
||||
try:
|
||||
last_listen = UserListenHistory.objects.filter(user_id=user_id).last()
|
||||
except UserListenHistory.DoesNotExist:
|
||||
last_listen = None
|
||||
if (
|
||||
last_listen
|
||||
and last_listen.song_id == song_id
|
||||
or last_listen
|
||||
and last_listen.created + timedelta(seconds=s.length) > now()
|
||||
):
|
||||
return
|
||||
UserListenHistory.objects.create(
|
||||
user_id=user_id,
|
||||
song_id=song_id,
|
||||
)
|
||||
try:
|
||||
user_profile = UserMusicProfile.objects.get(user_id=user_id)
|
||||
lastfm_token = user_profile.lastfm_token
|
||||
|
||||
# Initialize Last.fm network with the user's session key
|
||||
network = pylast.LastFMNetwork(
|
||||
api_key=settings.LAST_FM_API_KEY,
|
||||
api_secret=settings.LAST_FM_SECRET,
|
||||
session_key=lastfm_token,
|
||||
)
|
||||
song = Song.objects.get(id=song_id)
|
||||
artist_name = song.artists_names
|
||||
track_name = song.name
|
||||
timestamp = int(timezone.now().timestamp())
|
||||
network.scrobble(
|
||||
artist=artist_name, title=track_name, timestamp=timestamp
|
||||
)
|
||||
except UserMusicProfile.DoesNotExist:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Last.fm scrobble error: {e}")
|
||||
return song_id
|
||||
|
|
Loading…
Reference in New Issue
Block a user