Fix error

This commit is contained in:
2025-11-07 13:03:50 +11:00
parent d21dab8b60
commit 2bbf7e823d
2 changed files with 112 additions and 9 deletions
+74 -7
View File
@@ -1,5 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query
from fastapi.responses import FileResponse
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query, Request
from fastapi.responses import FileResponse, StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, or_
from typing import List, Optional
@@ -7,6 +7,7 @@ import os
import shutil
from pathlib import Path
import re
import logging
from app.db.session import get_db
from app.models.models import Music
@@ -15,6 +16,7 @@ from app.services.downloader import music_downloader
from app.core.config import settings
router = APIRouter()
logger = logging.getLogger(__name__)
def normalize_artist_name(artist: str) -> str:
@@ -398,14 +400,16 @@ async def scan_music_directory(db: AsyncSession = Depends(get_db)):
@router.get("/file/{music_id}")
async def serve_music_file(
music_id: int,
request: Request,
db: AsyncSession = Depends(get_db)
):
"""Serve music file - handles both regular and local music directory files"""
"""Serve music file with Range request support for audio streaming"""
# Get music record
result = await db.execute(select(Music).where(Music.id == music_id))
music = result.scalar_one_or_none()
if not music:
logger.error(f"Music ID {music_id} not found in database")
raise HTTPException(status_code=404, detail="Music not found")
# Determine file path
@@ -418,16 +422,79 @@ async def serve_music_file(
# Check if file exists
if not os.path.exists(file_path):
raise HTTPException(status_code=404, detail="File not found on disk")
logger.error(f"File not found on disk: {file_path} (Music ID: {music_id})")
raise HTTPException(status_code=404, detail=f"File not found: {music.file_path}")
# Return file
# Map file extensions to proper MIME types
mime_type_map = {
'mp3': 'audio/mpeg',
'mp4': 'audio/mp4',
'm4a': 'audio/mp4',
'wav': 'audio/wav',
'flac': 'audio/flac',
'ogg': 'audio/ogg',
'opus': 'audio/opus',
'webm': 'audio/webm',
'aac': 'audio/aac',
}
# Get MIME type based on file extension
file_ext = music.file_format or os.path.splitext(file_path)[1].lstrip('.')
media_type = mime_type_map.get(file_ext.lower(), 'audio/mpeg')
logger.info(f"Serving music file: {file_path} (ID: {music_id}, MIME: {media_type})")
# Return file with proper headers for audio streaming
return FileResponse(
path=file_path,
media_type=f"audio/{music.file_format or 'mpeg'}",
filename=os.path.basename(file_path)
media_type=media_type,
filename=os.path.basename(file_path),
headers={
'Accept-Ranges': 'bytes',
'Cache-Control': 'public, max-age=3600',
}
)
@router.get("/file/{music_id}/check")
async def check_music_file(
music_id: int,
db: AsyncSession = Depends(get_db)
):
"""Check if music file exists and is accessible (diagnostic endpoint)"""
# Get music record
result = await db.execute(select(Music).where(Music.id == music_id))
music = result.scalar_one_or_none()
if not music:
return {
"exists": False,
"error": "Music record not found in database",
"music_id": music_id
}
# Determine file path
if music.file_location and os.path.isabs(music.file_location):
file_path = music.file_location
else:
file_path = os.path.join(settings.MUSIC_DIR, music.file_path)
file_exists = os.path.exists(file_path)
return {
"exists": file_exists,
"music_id": music_id,
"title": music.title,
"file_path": music.file_path,
"full_path": file_path,
"file_location": music.file_location,
"file_format": music.file_format,
"music_dir": settings.MUSIC_DIR,
"file_size": os.path.getsize(file_path) if file_exists else None,
"is_readable": os.access(file_path, os.R_OK) if file_exists else False,
}
@router.get("/share/{share_token}", response_model=MusicSchema)
async def get_music_by_share_token(share_token: str, db: AsyncSession = Depends(get_db)):
"""Get music by share token (public endpoint) - checks expiration"""
+38 -2
View File
@@ -132,9 +132,45 @@ function App() {
// Check if it's a streaming URL
const isStreamUrl = currentMusic.file_path.startsWith('/api/stream') || currentMusic.file_path.startsWith('http')
// Use dedicated file serving endpoint for all local files
audioRef.current.src = isStreamUrl ? currentMusic.file_path : `/api/music/file/${currentMusic.id}`
const audioSrc = isStreamUrl ? currentMusic.file_path : `/api/music/file/${currentMusic.id}`
console.log(`Loading audio: ${currentMusic.title} (ID: ${currentMusic.id})`)
console.log(`Audio source: ${audioSrc}`)
audioRef.current.src = audioSrc
// Add error handler to help debug loading issues
const handleError = (e: Event) => {
console.error('Audio loading error:', {
musicId: currentMusic.id,
title: currentMusic.title,
src: audioSrc,
error: (e.target as HTMLAudioElement)?.error,
networkState: (e.target as HTMLAudioElement)?.networkState,
readyState: (e.target as HTMLAudioElement)?.readyState,
})
// Try fallback to direct /music/ path if API endpoint fails
if (!isStreamUrl && audioRef.current && !audioRef.current.src.includes('/music/')) {
const fallbackSrc = `/music/${currentMusic.file_path}`
console.log(`Trying fallback source: ${fallbackSrc}`)
audioRef.current.src = fallbackSrc
if (isPlaying) {
audioRef.current.play().catch(err => console.error('Fallback play error:', err))
}
}
}
audioRef.current.addEventListener('error', handleError)
if (isPlaying) {
audioRef.current.play()
audioRef.current.play().catch(err => {
console.error('Play error:', err)
})
}
return () => {
audioRef.current?.removeEventListener('error', handleError)
}
}
}, [currentMusic])