soundwave/backend/playlist/views_download.py

208 lines
7 KiB
Python
Raw Normal View History

"""Views for playlist downloads"""
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 django.shortcuts import get_object_or_404
from playlist.models import Playlist
from playlist.models_download import PlaylistDownload, PlaylistDownloadItem
from playlist.serializers_download import (
PlaylistDownloadSerializer,
PlaylistDownloadCreateSerializer,
PlaylistDownloadItemSerializer,
)
from playlist.tasks_download import (
download_playlist_task,
pause_playlist_download,
resume_playlist_download,
cancel_playlist_download,
retry_failed_items,
)
from common.permissions import IsOwnerOrAdmin
class PlaylistDownloadViewSet(viewsets.ModelViewSet):
"""ViewSet for managing playlist downloads"""
permission_classes = [IsAuthenticated, IsOwnerOrAdmin]
def get_serializer_class(self):
if self.action == 'create':
return PlaylistDownloadCreateSerializer
return PlaylistDownloadSerializer
def get_queryset(self):
"""Filter by user"""
queryset = PlaylistDownload.objects.select_related(
'playlist', 'user'
).prefetch_related('items')
# Regular users see only their downloads
if not (self.request.user.is_admin or self.request.user.is_superuser):
queryset = queryset.filter(user=self.request.user)
# Filter by status
status_filter = self.request.query_params.get('status')
if status_filter:
queryset = queryset.filter(status=status_filter)
# Filter by playlist
playlist_id = self.request.query_params.get('playlist_id')
if playlist_id:
queryset = queryset.filter(playlist_id=playlist_id)
return queryset.order_by('-created_at')
def perform_create(self, serializer):
"""Create download and trigger task"""
download = serializer.save(user=self.request.user)
# Trigger download task
download_playlist_task.apply_async(args=[download.id])
return download
@action(detail=True, methods=['post'])
def pause(self, request, pk=None):
"""Pause playlist download"""
download = self.get_object()
if download.status != 'downloading':
return Response(
{'error': 'Can only pause downloading playlists'},
status=status.HTTP_400_BAD_REQUEST
)
result = pause_playlist_download.apply_async(args=[download.id])
return Response({
'message': 'Playlist download paused',
'task_id': result.id
})
@action(detail=True, methods=['post'])
def resume(self, request, pk=None):
"""Resume paused playlist download"""
download = self.get_object()
if not download.can_resume:
return Response(
{'error': 'Download cannot be resumed'},
status=status.HTTP_400_BAD_REQUEST
)
result = resume_playlist_download.apply_async(args=[download.id])
return Response({
'message': 'Playlist download resumed',
'task_id': result.id
})
@action(detail=True, methods=['post'])
def cancel(self, request, pk=None):
"""Cancel playlist download"""
download = self.get_object()
if download.status in ['completed', 'failed']:
return Response(
{'error': 'Cannot cancel completed or failed download'},
status=status.HTTP_400_BAD_REQUEST
)
result = cancel_playlist_download.apply_async(args=[download.id])
return Response({
'message': 'Playlist download cancelled',
'task_id': result.id
})
@action(detail=True, methods=['post'])
def retry_failed(self, request, pk=None):
"""Retry failed items"""
download = self.get_object()
if download.failed_items == 0:
return Response(
{'error': 'No failed items to retry'},
status=status.HTTP_400_BAD_REQUEST
)
result = retry_failed_items.apply_async(args=[download.id])
return Response({
'message': f'Retrying {download.failed_items} failed items',
'task_id': result.id
})
@action(detail=True, methods=['get'])
def items(self, request, pk=None):
"""Get download items with status"""
download = self.get_object()
items = download.items.select_related('audio').order_by('position')
serializer = PlaylistDownloadItemSerializer(items, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def active(self, request):
"""Get active downloads (pending or downloading)"""
downloads = self.get_queryset().filter(
status__in=['pending', 'downloading']
)
serializer = self.get_serializer(downloads, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def completed(self, request):
"""Get completed downloads"""
downloads = self.get_queryset().filter(status='completed')
serializer = self.get_serializer(downloads, many=True)
return Response(serializer.data)
@action(detail=False, methods=['post'])
def download_playlist(self, request):
"""Quick action to download a playlist"""
playlist_id = request.data.get('playlist_id')
quality = request.data.get('quality', 'medium')
if not playlist_id:
return Response(
{'error': 'playlist_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
# Get playlist
playlist = get_object_or_404(Playlist, id=playlist_id, owner=request.user)
# Check if already downloading
existing = PlaylistDownload.objects.filter(
playlist=playlist,
user=request.user,
status__in=['pending', 'downloading']
).first()
if existing:
return Response(
{
'error': 'Playlist is already being downloaded',
'download': PlaylistDownloadSerializer(existing).data
},
status=status.HTTP_400_BAD_REQUEST
)
# Create download
download = PlaylistDownload.objects.create(
playlist=playlist,
user=request.user,
quality=quality
)
# Trigger task
download_playlist_task.apply_async(args=[download.id])
serializer = PlaylistDownloadSerializer(download)
return Response(serializer.data, status=status.HTTP_201_CREATED)