mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2025-09-14 12:22:26 +03:00
updated song liked
This commit is contained in:
parent
03c05d7adf
commit
e394705eca
|
@ -279,39 +279,33 @@ def validate(self, attrs):
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
song = Song.objects.get(slug=validated_data["song"])
|
song = Song.objects.get(slug=validated_data["song"])
|
||||||
if self.context["like"]:
|
user = self.context["request"].user
|
||||||
if SongUserRating.objects.filter(
|
is_like_action = self.context["like"]
|
||||||
song=song, user=self.context["request"].user
|
|
||||||
).exists():
|
try:
|
||||||
song_user_rating = SongUserRating.objects.get(
|
existing_rating = SongUserRating.objects.get(song=song, user=user)
|
||||||
song=song, user=self.context["request"].user
|
|
||||||
|
if is_like_action:
|
||||||
|
if existing_rating.like:
|
||||||
|
existing_rating.delete()
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
existing_rating.like = True
|
||||||
|
existing_rating.save()
|
||||||
|
return existing_rating
|
||||||
|
else:
|
||||||
|
if not existing_rating.like:
|
||||||
|
existing_rating.delete()
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
existing_rating.like = False
|
||||||
|
existing_rating.save()
|
||||||
|
return existing_rating
|
||||||
|
|
||||||
|
except SongUserRating.DoesNotExist:
|
||||||
|
return SongUserRating.objects.create(
|
||||||
|
song=song, user=user, like=is_like_action
|
||||||
)
|
)
|
||||||
if song_user_rating.like:
|
|
||||||
song_user_rating.delete()
|
|
||||||
else:
|
|
||||||
song_user_rating.like = True
|
|
||||||
song_user_rating.save()
|
|
||||||
else:
|
|
||||||
song_user_rating = SongUserRating.objects.create(
|
|
||||||
song=song, user=self.context["request"].user, like=True
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if SongUserRating.objects.filter(
|
|
||||||
song=song, user=self.context["request"].user
|
|
||||||
).exists():
|
|
||||||
song_user_rating = SongUserRating.objects.get(
|
|
||||||
song=song, user=self.context["request"].user
|
|
||||||
)
|
|
||||||
if not song_user_rating.like:
|
|
||||||
song_user_rating.delete()
|
|
||||||
else:
|
|
||||||
song_user_rating.like = False
|
|
||||||
song_user_rating.save()
|
|
||||||
else:
|
|
||||||
song_user_rating = SongUserRating.objects.create(
|
|
||||||
song=song, user=self.context["request"].user, like=False
|
|
||||||
)
|
|
||||||
return song_user_rating
|
|
||||||
|
|
||||||
|
|
||||||
class ListSongSlugsSerializer(serializers.Serializer):
|
class ListSongSlugsSerializer(serializers.Serializer):
|
||||||
|
|
|
@ -323,7 +323,7 @@ def get_serializer_context(self, **kwargs):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class LikeSongAPIView(generics.CreateAPIView):
|
class LikeSongAPIView(generics.GenericAPIView):
|
||||||
serializer_class = LikeDislikeSongSerializer
|
serializer_class = LikeDislikeSongSerializer
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
@ -335,8 +335,19 @@ def get_serializer_context(self, **kwargs):
|
||||||
context["like"] = True
|
context["like"] = True
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
class DislikeSongAPIView(generics.CreateAPIView):
|
result = serializer.save()
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return Response({"message": "Like removed"}, status=200)
|
||||||
|
else:
|
||||||
|
return Response({"message": "Song liked", "liked": result.like}, status=201)
|
||||||
|
|
||||||
|
|
||||||
|
class DislikeSongAPIView(generics.GenericAPIView):
|
||||||
serializer_class = LikeDislikeSongSerializer
|
serializer_class = LikeDislikeSongSerializer
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
@ -348,6 +359,19 @@ def get_serializer_context(self, **kwargs):
|
||||||
context["like"] = False
|
context["like"] = False
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
result = serializer.save()
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return Response({"message": "Dislike removed"}, status=200)
|
||||||
|
else:
|
||||||
|
return Response(
|
||||||
|
{"message": "Song disliked", "liked": result.like}, status=201
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ListAlbumsAPIView(generics.ListAPIView):
|
class ListAlbumsAPIView(generics.ListAPIView):
|
||||||
serializer_class = ListAlbumSerializer
|
serializer_class = ListAlbumSerializer
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -9,7 +10,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.db.models import Count
|
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from mutagen.id3 import ID3
|
from mutagen.id3 import ID3
|
||||||
from mutagen.mp3 import MP3
|
from mutagen.mp3 import MP3
|
||||||
|
@ -259,39 +259,6 @@ def clean_title_for_slug(title: str) -> str:
|
||||||
return cleaned.strip()
|
return cleaned.strip()
|
||||||
|
|
||||||
|
|
||||||
def process_authors_string(authors_str: str) -> list[str]:
|
|
||||||
"""Split author string into individual author names."""
|
|
||||||
if not authors_str:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# First split by major separators
|
|
||||||
authors = []
|
|
||||||
for part in authors_str.split("/"):
|
|
||||||
for subpart in part.split("&"):
|
|
||||||
# Handle various featuring cases
|
|
||||||
for feat_marker in [
|
|
||||||
" feat.",
|
|
||||||
" ft.",
|
|
||||||
" featuring.",
|
|
||||||
" presents ",
|
|
||||||
" pres. ",
|
|
||||||
]:
|
|
||||||
if feat_marker in subpart.lower():
|
|
||||||
parts = subpart.lower().split(feat_marker, 1)
|
|
||||||
authors.extend(part.strip() for part in parts)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Handle collaboration markers
|
|
||||||
if " x " in subpart:
|
|
||||||
authors.extend(p.strip() for p in subpart.split(" x "))
|
|
||||||
else:
|
|
||||||
authors.append(subpart.strip())
|
|
||||||
|
|
||||||
# Remove duplicates while preserving order
|
|
||||||
seen = set()
|
|
||||||
return [x for x in authors if not (x.lower() in seen or seen.add(x.lower()))]
|
|
||||||
|
|
||||||
|
|
||||||
def extract_mp3_metadata(file_path: str) -> dict | None:
|
def extract_mp3_metadata(file_path: str) -> dict | None:
|
||||||
"""Extract metadata from MP3 file."""
|
"""Extract metadata from MP3 file."""
|
||||||
try:
|
try:
|
||||||
|
@ -442,20 +409,64 @@ def get_or_create_album(album_name: str, authors: list[Author]) -> Album | None:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def process_authors_string(authors_str: str) -> list[str]:
|
||||||
|
"""Split author string into individual author names."""
|
||||||
|
if not authors_str:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# First split by major delimiters
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
# Split by common delimiters while preserving Asian character sequences
|
||||||
|
for part in re.split(r"[,&/](?![^\[]*\])", authors_str):
|
||||||
|
# Clean up each part
|
||||||
|
cleaned = part.strip()
|
||||||
|
|
||||||
|
# Handle featuring cases
|
||||||
|
featuring_markers = [" feat.", " ft.", " featuring", " presents ", " pres. "]
|
||||||
|
for marker in featuring_markers:
|
||||||
|
if marker in cleaned.lower():
|
||||||
|
main_artist, feat_artist = cleaned.lower().split(marker, 1)
|
||||||
|
authors.extend(
|
||||||
|
[a.strip() for a in [main_artist, feat_artist] if a.strip()]
|
||||||
|
)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Handle collaborations with 'x' or 'X'
|
||||||
|
if " x " in cleaned or " X " in cleaned:
|
||||||
|
collab_parts = re.split(" [xX] ", cleaned)
|
||||||
|
authors.extend(p.strip() for p in collab_parts if p.strip())
|
||||||
|
else:
|
||||||
|
if cleaned:
|
||||||
|
authors.append(cleaned)
|
||||||
|
|
||||||
|
# Remove duplicates while preserving order
|
||||||
|
seen = set()
|
||||||
|
return [x for x in authors if not (x.lower() in seen or seen.add(x.lower()))]
|
||||||
|
|
||||||
|
|
||||||
def check_song_exists(title: str, album: Album | None, authors: list[Author]) -> bool:
|
def check_song_exists(title: str, album: Album | None, authors: list[Author]) -> bool:
|
||||||
"""Check if a song already exists with the given title, album and authors."""
|
"""Check if a song already exists with the given title, album and authors."""
|
||||||
|
if not title:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Start with base query
|
||||||
query = Song.objects.filter(name__iexact=title)
|
query = Song.objects.filter(name__iexact=title)
|
||||||
|
|
||||||
if album:
|
if album:
|
||||||
query = query.filter(album=album)
|
query = query.filter(album=album)
|
||||||
|
|
||||||
if authors:
|
if authors:
|
||||||
# Ensure exact author match
|
# Get all author IDs
|
||||||
query = query.annotate(author_count=Count("authors")).filter(
|
author_ids = {author.id for author in authors}
|
||||||
author_count=len(authors), authors__in=authors
|
|
||||||
)
|
|
||||||
|
|
||||||
return query.exists()
|
# Filter songs that have exactly these authors
|
||||||
|
for song in query:
|
||||||
|
song_author_ids = set(song.authors.values_list("id", flat=True))
|
||||||
|
if song_author_ids == author_ids:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def load_mp3_directory(
|
def load_mp3_directory(
|
||||||
|
@ -507,10 +518,10 @@ def load_mp3_directory(
|
||||||
failed_files.append(str(mp3_path))
|
failed_files.append(str(mp3_path))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check for existing song
|
# Check for existing song with exact matches
|
||||||
if check_song_exists(metadata["title"], album, authors):
|
if check_song_exists(metadata["title"], album, authors):
|
||||||
print(
|
print(
|
||||||
f"Skipping existing song: {metadata['artists']} - {metadata['title']}"
|
f"Skipping existing song: {' & '.join(author_names)} - {metadata['title']}"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -526,13 +537,17 @@ def load_mp3_directory(
|
||||||
f"{album.slug}.png", File(img_file), save=True
|
f"{album.slug}.png", File(img_file), save=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create song with proper slug from file name
|
# Create song with proper slug
|
||||||
file_name = os.path.splitext(os.path.basename(str(mp3_path)))[0]
|
_name = (
|
||||||
|
metadata["title"]
|
||||||
|
if metadata["title"]
|
||||||
|
else os.path.splitext(os.path.basename(str(mp3_path)))[0]
|
||||||
|
)
|
||||||
song = Song(
|
song = Song(
|
||||||
name=metadata["title"],
|
name=metadata["title"],
|
||||||
length=metadata["length"],
|
length=metadata["length"],
|
||||||
album=album,
|
album=album,
|
||||||
slug=generate_unique_slug(file_name, Song),
|
slug=generate_unique_slug(_name, Song),
|
||||||
creator=User.objects.get(id=user_id) if user_id else None,
|
creator=User.objects.get(id=user_id) if user_id else None,
|
||||||
meta={
|
meta={
|
||||||
"genre": metadata["genre"],
|
"genre": metadata["genre"],
|
||||||
|
@ -560,7 +575,7 @@ def load_mp3_directory(
|
||||||
print(f"Error processing {mp3_path}: {str(e)}")
|
print(f"Error processing {mp3_path}: {str(e)}")
|
||||||
failed_files.append(str(mp3_path))
|
failed_files.append(str(mp3_path))
|
||||||
|
|
||||||
# Trigger image cropping for all created/updated objects
|
# Process image cropping for all created/updated objects
|
||||||
print("Processing image cropping...")
|
print("Processing image cropping...")
|
||||||
|
|
||||||
for author_id in created_authors:
|
for author_id in created_authors:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user