Initial commit - SoundWave v1.0
- Full PWA support with offline capabilities - Comprehensive search across songs, playlists, and channels - Offline playlist manager with download tracking - Pre-built frontend for zero-build deployment - Docker-based deployment with docker compose - Material-UI dark theme interface - YouTube audio download and management - Multi-user authentication support
This commit is contained in:
commit
51679d1943
254 changed files with 37281 additions and 0 deletions
139
backend/playlist/models_download.py
Normal file
139
backend/playlist/models_download.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
"""Models for playlist download management"""
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth import get_user_model
|
||||
from playlist.models import Playlist
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class PlaylistDownload(models.Model):
|
||||
"""Track playlist download for offline playback"""
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('pending', 'Pending'),
|
||||
('downloading', 'Downloading'),
|
||||
('completed', 'Completed'),
|
||||
('failed', 'Failed'),
|
||||
('paused', 'Paused'),
|
||||
]
|
||||
|
||||
playlist = models.ForeignKey(
|
||||
Playlist,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='downloads'
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='playlist_downloads'
|
||||
)
|
||||
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
|
||||
|
||||
# Progress tracking
|
||||
total_items = models.IntegerField(default=0)
|
||||
downloaded_items = models.IntegerField(default=0)
|
||||
failed_items = models.IntegerField(default=0)
|
||||
|
||||
# Size tracking
|
||||
total_size_bytes = models.BigIntegerField(default=0, help_text="Total size in bytes")
|
||||
downloaded_size_bytes = models.BigIntegerField(default=0, help_text="Downloaded size in bytes")
|
||||
|
||||
# Download settings
|
||||
quality = models.CharField(
|
||||
max_length=20,
|
||||
default='medium',
|
||||
choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('ultra', 'Ultra')]
|
||||
)
|
||||
|
||||
# Timestamps
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
started_at = models.DateTimeField(null=True, blank=True)
|
||||
completed_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
# Error tracking
|
||||
error_message = models.TextField(blank=True)
|
||||
|
||||
# Download location
|
||||
download_path = models.CharField(max_length=500, blank=True, help_text="Path to downloaded files")
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
unique_together = ('playlist', 'user')
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'status']),
|
||||
models.Index(fields=['playlist', 'status']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username} - {self.playlist.title} ({self.status})"
|
||||
|
||||
@property
|
||||
def progress_percent(self):
|
||||
"""Calculate download progress percentage"""
|
||||
if self.total_items == 0:
|
||||
return 0
|
||||
return (self.downloaded_items / self.total_items) * 100
|
||||
|
||||
@property
|
||||
def is_complete(self):
|
||||
"""Check if download is complete"""
|
||||
return self.status == 'completed'
|
||||
|
||||
@property
|
||||
def can_resume(self):
|
||||
"""Check if download can be resumed"""
|
||||
return self.status in ['paused', 'failed']
|
||||
|
||||
|
||||
class PlaylistDownloadItem(models.Model):
|
||||
"""Track individual audio items in playlist download"""
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('pending', 'Pending'),
|
||||
('downloading', 'Downloading'),
|
||||
('completed', 'Completed'),
|
||||
('failed', 'Failed'),
|
||||
('skipped', 'Skipped'),
|
||||
]
|
||||
|
||||
download = models.ForeignKey(
|
||||
PlaylistDownload,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='items'
|
||||
)
|
||||
audio = models.ForeignKey(
|
||||
'audio.Audio',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='playlist_download_items'
|
||||
)
|
||||
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
|
||||
position = models.IntegerField(default=0)
|
||||
|
||||
# Progress tracking
|
||||
file_size_bytes = models.BigIntegerField(default=0)
|
||||
downloaded_bytes = models.BigIntegerField(default=0)
|
||||
|
||||
# Timestamps
|
||||
started_at = models.DateTimeField(null=True, blank=True)
|
||||
completed_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
# Error tracking
|
||||
error_message = models.TextField(blank=True)
|
||||
retry_count = models.IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ['position']
|
||||
unique_together = ('download', 'audio')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.download.playlist.title} - {self.audio.title} ({self.status})"
|
||||
|
||||
@property
|
||||
def progress_percent(self):
|
||||
"""Calculate item download progress"""
|
||||
if self.file_size_bytes == 0:
|
||||
return 0
|
||||
return (self.downloaded_bytes / self.file_size_bytes) * 100
|
||||
Loading…
Add table
Add a link
Reference in a new issue