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
186
backend/audio/serializers_local.py
Normal file
186
backend/audio/serializers_local.py
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
"""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
|
||||
Loading…
Add table
Add a link
Reference in a new issue