soundwave/backend/audio/serializers_local.py

187 lines
6.2 KiB
Python
Raw Normal View History

"""Serializers for local audio files"""
from rest_framework import serializers
from audio.models_local import LocalAudio, LocalAudioPlaylist, LocalAudioPlaylistItem
class LocalAudioSerializer(serializers.ModelSerializer):
"""Serializer for local audio files"""
file_size_mb = serializers.FloatField(read_only=True)
duration_formatted = serializers.CharField(read_only=True)
file_url = serializers.SerializerMethodField()
cover_art_url = serializers.SerializerMethodField()
class Meta:
model = LocalAudio
fields = [
'id',
'title',
'artist',
'album',
'year',
'genre',
'track_number',
'file',
'file_url',
'file_size',
'file_size_mb',
'duration',
'duration_formatted',
'audio_format',
'bitrate',
'sample_rate',
'channels',
'cover_art',
'cover_art_url',
'original_filename',
'uploaded_date',
'modified_date',
'play_count',
'last_played',
'tags',
'notes',
'is_favorite',
]
read_only_fields = [
'id',
'file_size',
'duration',
'audio_format',
'bitrate',
'sample_rate',
'channels',
'original_filename',
'uploaded_date',
'modified_date',
'play_count',
'last_played',
]
def get_file_url(self, obj):
"""Get full URL for audio file"""
request = self.context.get('request')
if obj.file and request:
return request.build_absolute_uri(obj.file.url)
return None
def get_cover_art_url(self, obj):
"""Get full URL for cover art"""
request = self.context.get('request')
if obj.cover_art and request:
return request.build_absolute_uri(obj.cover_art.url)
return None
class LocalAudioUploadSerializer(serializers.ModelSerializer):
"""Serializer for uploading local audio files"""
# Make title optional - will be extracted from ID3 tags if not provided
title = serializers.CharField(required=False, allow_blank=True, max_length=500)
class Meta:
model = LocalAudio
fields = [
'file',
'title',
'artist',
'album',
'year',
'genre',
'track_number',
'cover_art',
'tags',
'notes',
]
def create(self, validated_data):
"""Extract metadata and create local audio"""
file = validated_data['file']
# Set original filename
validated_data['original_filename'] = file.name
validated_data['file_size'] = file.size
# Extract audio format from filename
file_extension = file.name.split('.')[-1].lower()
validated_data['audio_format'] = file_extension
# Try to extract metadata using mutagen
try:
from mutagen import File as MutagenFile
audio_file = MutagenFile(file)
if audio_file:
# Get duration
if hasattr(audio_file.info, 'length'):
validated_data['duration'] = int(audio_file.info.length)
# Get bitrate
if hasattr(audio_file.info, 'bitrate'):
validated_data['bitrate'] = int(audio_file.info.bitrate / 1000) # Convert to kbps
# Get sample rate
if hasattr(audio_file.info, 'sample_rate'):
validated_data['sample_rate'] = audio_file.info.sample_rate
# Get channels
if hasattr(audio_file.info, 'channels'):
validated_data['channels'] = audio_file.info.channels
# Extract ID3 tags if not provided
if not validated_data.get('title'):
validated_data['title'] = audio_file.get('TIT2', [file.name])[0] if hasattr(audio_file, 'get') else file.name
if not validated_data.get('artist'):
validated_data['artist'] = audio_file.get('TPE1', [''])[0] if hasattr(audio_file, 'get') else ''
if not validated_data.get('album'):
validated_data['album'] = audio_file.get('TALB', [''])[0] if hasattr(audio_file, 'get') else ''
except Exception as e:
# If metadata extraction fails, use filename as title
if not validated_data.get('title'):
validated_data['title'] = file.name
return super().create(validated_data)
class LocalAudioPlaylistItemSerializer(serializers.ModelSerializer):
"""Serializer for playlist items"""
audio_data = LocalAudioSerializer(source='audio', read_only=True)
class Meta:
model = LocalAudioPlaylistItem
fields = ['id', 'audio', 'audio_data', 'position', 'added_date']
read_only_fields = ['id', 'added_date']
class LocalAudioPlaylistSerializer(serializers.ModelSerializer):
"""Serializer for local audio playlists"""
items = LocalAudioPlaylistItemSerializer(many=True, read_only=True)
items_count = serializers.SerializerMethodField()
cover_image_url = serializers.SerializerMethodField()
class Meta:
model = LocalAudioPlaylist
fields = [
'id',
'title',
'description',
'cover_image',
'cover_image_url',
'created_date',
'modified_date',
'items',
'items_count',
]
read_only_fields = ['id', 'created_date', 'modified_date']
def get_items_count(self, obj):
"""Get number of items in playlist"""
return obj.items.count()
def get_cover_image_url(self, obj):
"""Get full URL for cover image"""
request = self.context.get('request')
if obj.cover_image and request:
return request.build_absolute_uri(obj.cover_image.url)
return None