Fix: Include backend/audio Django app in repository
This commit is contained in:
parent
d04e726373
commit
644cfab298
37 changed files with 6632 additions and 4 deletions
276
backend/audio/views_local.py
Normal file
276
backend/audio/views_local.py
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
"""Views for local audio files"""
|
||||
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from django.utils import timezone
|
||||
from django.db.models import Q
|
||||
|
||||
from audio.models_local import LocalAudio, LocalAudioPlaylist, LocalAudioPlaylistItem
|
||||
from audio.serializers_local import (
|
||||
LocalAudioSerializer,
|
||||
LocalAudioUploadSerializer,
|
||||
LocalAudioPlaylistSerializer,
|
||||
LocalAudioPlaylistItemSerializer,
|
||||
)
|
||||
from common.permissions import IsOwnerOrAdmin
|
||||
|
||||
|
||||
class LocalAudioViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for managing local audio files"""
|
||||
permission_classes = [IsAuthenticated, IsOwnerOrAdmin]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'create':
|
||||
return LocalAudioUploadSerializer
|
||||
return LocalAudioSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
"""Filter by user"""
|
||||
queryset = LocalAudio.objects.all()
|
||||
|
||||
# Regular users see only their files
|
||||
if not (self.request.user.is_admin or self.request.user.is_superuser):
|
||||
queryset = queryset.filter(owner=self.request.user)
|
||||
|
||||
# Search filter
|
||||
search = self.request.query_params.get('search')
|
||||
if search:
|
||||
queryset = queryset.filter(
|
||||
Q(title__icontains=search) |
|
||||
Q(artist__icontains=search) |
|
||||
Q(album__icontains=search) |
|
||||
Q(genre__icontains=search)
|
||||
)
|
||||
|
||||
# Filter by artist
|
||||
artist = self.request.query_params.get('artist')
|
||||
if artist:
|
||||
queryset = queryset.filter(artist__icontains=artist)
|
||||
|
||||
# Filter by album
|
||||
album = self.request.query_params.get('album')
|
||||
if album:
|
||||
queryset = queryset.filter(album__icontains=album)
|
||||
|
||||
# Filter by genre
|
||||
genre = self.request.query_params.get('genre')
|
||||
if genre:
|
||||
queryset = queryset.filter(genre__icontains=genre)
|
||||
|
||||
# Filter by favorites
|
||||
favorites = self.request.query_params.get('favorites')
|
||||
if favorites == 'true':
|
||||
queryset = queryset.filter(is_favorite=True)
|
||||
|
||||
# Filter by tags
|
||||
tags = self.request.query_params.get('tags')
|
||||
if tags:
|
||||
tag_list = tags.split(',')
|
||||
for tag in tag_list:
|
||||
queryset = queryset.filter(tags__contains=[tag.strip()])
|
||||
|
||||
return queryset.order_by('-uploaded_date')
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Set owner on creation"""
|
||||
user = self.request.user
|
||||
|
||||
# Check storage quota
|
||||
if not (user.is_admin or user.is_superuser):
|
||||
if user.storage_used_gb >= user.storage_quota_gb:
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
raise PermissionDenied(f"Storage quota exceeded ({user.storage_used_gb:.1f} / {user.storage_quota_gb} GB)")
|
||||
|
||||
local_audio = serializer.save(owner=user)
|
||||
|
||||
# Update user storage
|
||||
file_size_gb = local_audio.file_size / (1024 ** 3)
|
||||
user.storage_used_gb += file_size_gb
|
||||
user.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""Update storage on deletion"""
|
||||
user = instance.owner
|
||||
file_size_gb = instance.file_size / (1024 ** 3)
|
||||
|
||||
# Delete the instance
|
||||
instance.delete()
|
||||
|
||||
# Update user storage
|
||||
user.storage_used_gb = max(0, user.storage_used_gb - file_size_gb)
|
||||
user.save()
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def play(self, request, pk=None):
|
||||
"""Increment play count"""
|
||||
audio = self.get_object()
|
||||
audio.play_count += 1
|
||||
audio.last_played = timezone.now()
|
||||
audio.save()
|
||||
|
||||
return Response({'message': 'Play count updated'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def toggle_favorite(self, request, pk=None):
|
||||
"""Toggle favorite status"""
|
||||
audio = self.get_object()
|
||||
audio.is_favorite = not audio.is_favorite
|
||||
audio.save()
|
||||
|
||||
return Response({
|
||||
'message': 'Favorite status updated',
|
||||
'is_favorite': audio.is_favorite
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def artists(self, request):
|
||||
"""Get list of artists"""
|
||||
queryset = self.get_queryset()
|
||||
artists = queryset.values_list('artist', flat=True).distinct().order_by('artist')
|
||||
artists = [a for a in artists if a] # Remove empty strings
|
||||
|
||||
return Response(artists)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def albums(self, request):
|
||||
"""Get list of albums"""
|
||||
queryset = self.get_queryset()
|
||||
albums = queryset.values('album', 'artist').distinct().order_by('album')
|
||||
albums = [a for a in albums if a['album']] # Remove empty albums
|
||||
|
||||
return Response(albums)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def genres(self, request):
|
||||
"""Get list of genres"""
|
||||
queryset = self.get_queryset()
|
||||
genres = queryset.values_list('genre', flat=True).distinct().order_by('genre')
|
||||
genres = [g for g in genres if g] # Remove empty strings
|
||||
|
||||
return Response(genres)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def stats(self, request):
|
||||
"""Get statistics"""
|
||||
queryset = self.get_queryset()
|
||||
|
||||
stats = {
|
||||
'total_files': queryset.count(),
|
||||
'total_artists': queryset.values('artist').distinct().count(),
|
||||
'total_albums': queryset.values('album').distinct().count(),
|
||||
'total_duration': sum(a.duration or 0 for a in queryset),
|
||||
'total_size_mb': sum(a.file_size for a in queryset) / (1024 * 1024),
|
||||
'favorites': queryset.filter(is_favorite=True).count(),
|
||||
}
|
||||
|
||||
return Response(stats)
|
||||
|
||||
|
||||
class LocalAudioPlaylistViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for managing local audio playlists"""
|
||||
permission_classes = [IsAuthenticated, IsOwnerOrAdmin]
|
||||
serializer_class = LocalAudioPlaylistSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
"""Filter by user"""
|
||||
queryset = LocalAudioPlaylist.objects.prefetch_related('items__audio')
|
||||
|
||||
# Regular users see only their playlists
|
||||
if not (self.request.user.is_admin or self.request.user.is_superuser):
|
||||
queryset = queryset.filter(owner=self.request.user)
|
||||
|
||||
return queryset.order_by('-created_date')
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Set owner on creation"""
|
||||
serializer.save(owner=self.request.user)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def add_item(self, request, pk=None):
|
||||
"""Add audio to playlist"""
|
||||
playlist = self.get_object()
|
||||
audio_id = request.data.get('audio_id')
|
||||
|
||||
if not audio_id:
|
||||
return Response(
|
||||
{'error': 'audio_id is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
audio = LocalAudio.objects.get(id=audio_id, owner=request.user)
|
||||
except LocalAudio.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'Audio not found'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
# Get next position
|
||||
last_item = playlist.items.order_by('-position').first()
|
||||
position = (last_item.position + 1) if last_item else 0
|
||||
|
||||
# Create item
|
||||
item, created = LocalAudioPlaylistItem.objects.get_or_create(
|
||||
playlist=playlist,
|
||||
audio=audio,
|
||||
defaults={'position': position}
|
||||
)
|
||||
|
||||
if not created:
|
||||
return Response(
|
||||
{'error': 'Audio already in playlist'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
serializer = LocalAudioPlaylistItemSerializer(item, context={'request': request})
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def remove_item(self, request, pk=None):
|
||||
"""Remove audio from playlist"""
|
||||
playlist = self.get_object()
|
||||
audio_id = request.data.get('audio_id')
|
||||
|
||||
if not audio_id:
|
||||
return Response(
|
||||
{'error': 'audio_id is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
item = LocalAudioPlaylistItem.objects.get(
|
||||
playlist=playlist,
|
||||
audio_id=audio_id
|
||||
)
|
||||
item.delete()
|
||||
return Response({'message': 'Item removed from playlist'})
|
||||
except LocalAudioPlaylistItem.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'Item not found in playlist'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def reorder(self, request, pk=None):
|
||||
"""Reorder playlist items"""
|
||||
playlist = self.get_object()
|
||||
item_order = request.data.get('item_order', [])
|
||||
|
||||
if not item_order:
|
||||
return Response(
|
||||
{'error': 'item_order is required (array of item IDs)'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# Update positions
|
||||
for position, item_id in enumerate(item_order):
|
||||
LocalAudioPlaylistItem.objects.filter(
|
||||
playlist=playlist,
|
||||
id=item_id
|
||||
).update(position=position)
|
||||
|
||||
return Response({'message': 'Playlist reordered'})
|
||||
Loading…
Add table
Add a link
Reference in a new issue