Fix CORS #3
@@ -5,7 +5,9 @@ POSTGRES_PASSWORD=audio_password
|
||||
POSTGRES_DB=audio_classifier
|
||||
|
||||
# Backend API
|
||||
CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
|
||||
# Use "*" to allow all origins (recommended for development/local deployment)
|
||||
# Or specify comma-separated URLs for production: http://yourdomain.com,https://yourdomain.com
|
||||
CORS_ORIGINS=*
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
|
||||
@@ -16,4 +18,5 @@ ESSENTIA_MODELS_PATH=/app/models
|
||||
AUDIO_LIBRARY_PATH=/path/to/your/audio/library
|
||||
|
||||
# Frontend
|
||||
NEXT_PUBLIC_API_URL=http://localhost:8000
|
||||
# API URL accessed by the browser (use port 8001 since backend is mapped to 8001)
|
||||
NEXT_PUBLIC_API_URL=http://localhost:8001
|
||||
|
||||
@@ -10,7 +10,8 @@ class Settings(BaseSettings):
|
||||
DATABASE_URL: str = "postgresql://audio_user:audio_password@localhost:5432/audio_classifier"
|
||||
|
||||
# API Configuration
|
||||
CORS_ORIGINS: str = "http://localhost:3000,http://127.0.0.1:3000"
|
||||
# Comma-separated list of allowed origins, or use "*" to allow all
|
||||
CORS_ORIGINS: str = "*"
|
||||
API_HOST: str = "0.0.0.0"
|
||||
API_PORT: int = 8000
|
||||
|
||||
@@ -33,7 +34,13 @@ class Settings(BaseSettings):
|
||||
|
||||
@property
|
||||
def cors_origins_list(self) -> List[str]:
|
||||
"""Parse CORS origins string to list."""
|
||||
"""Parse CORS origins string to list.
|
||||
|
||||
If CORS_ORIGINS is "*", allow all origins.
|
||||
Otherwise, parse comma-separated list.
|
||||
"""
|
||||
if self.CORS_ORIGINS.strip() == "*":
|
||||
return ["*"]
|
||||
return [origin.strip() for origin in self.CORS_ORIGINS.split(",")]
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ services:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-audio_user}:${POSTGRES_PASSWORD:-audio_password}@postgres:5432/${POSTGRES_DB:-audio_classifier}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS:-*}
|
||||
ANALYSIS_USE_CLAP: ${ANALYSIS_USE_CLAP:-false}
|
||||
ANALYSIS_NUM_WORKERS: ${ANALYSIS_NUM_WORKERS:-4}
|
||||
ESSENTIA_MODELS_PATH: /app/models
|
||||
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-audio_user}:${POSTGRES_PASSWORD:-audio_password}@postgres:5432/${POSTGRES_DB:-audio_classifier}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS:-*}
|
||||
ANALYSIS_USE_CLAP: ${ANALYSIS_USE_CLAP:-false}
|
||||
ANALYSIS_NUM_WORKERS: ${ANALYSIS_NUM_WORKERS:-4}
|
||||
ESSENTIA_MODELS_PATH: /app/models
|
||||
|
||||
@@ -52,6 +52,7 @@ export default function Home() {
|
||||
const [filters, setFilters] = useState<FilterParams>({})
|
||||
const [page, setPage] = useState(0)
|
||||
const [currentTrack, setCurrentTrack] = useState<Track | null>(null)
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [isScanning, setIsScanning] = useState(false)
|
||||
const [scanStatus, setScanStatus] = useState<string>("")
|
||||
@@ -233,10 +234,19 @@ export default function Home() {
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Play button */}
|
||||
<button
|
||||
onClick={() => setCurrentTrack(track)}
|
||||
onClick={() => {
|
||||
if (currentTrack?.id === track.id) {
|
||||
// Toggle play/pause for current track
|
||||
setIsPlaying(!isPlaying)
|
||||
} else {
|
||||
// Switch to new track and start playing
|
||||
setCurrentTrack(track)
|
||||
setIsPlaying(true)
|
||||
}
|
||||
}}
|
||||
className="flex-shrink-0 w-12 h-12 flex items-center justify-center bg-orange-500 hover:bg-orange-600 rounded-full transition-colors shadow-sm"
|
||||
>
|
||||
{currentTrack?.id === track.id ? (
|
||||
{currentTrack?.id === track.id && isPlaying ? (
|
||||
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
|
||||
</svg>
|
||||
@@ -347,7 +357,11 @@ export default function Home() {
|
||||
|
||||
{/* Fixed Audio Player at bottom */}
|
||||
<div className="fixed bottom-0 left-0 right-0 z-50">
|
||||
<AudioPlayer track={currentTrack} />
|
||||
<AudioPlayer
|
||||
track={currentTrack}
|
||||
isPlaying={isPlaying}
|
||||
onPlayingChange={setIsPlaying}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -5,10 +5,11 @@ import type { Track } from "@/lib/types"
|
||||
|
||||
interface AudioPlayerProps {
|
||||
track: Track | null
|
||||
isPlaying: boolean
|
||||
onPlayingChange: (playing: boolean) => void
|
||||
}
|
||||
|
||||
export default function AudioPlayer({ track }: AudioPlayerProps) {
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
export default function AudioPlayer({ track, isPlaying, onPlayingChange }: AudioPlayerProps) {
|
||||
const [currentTime, setCurrentTime] = useState(0)
|
||||
const [duration, setDuration] = useState(0)
|
||||
const [volume, setVolume] = useState(1)
|
||||
@@ -22,7 +23,7 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
||||
// Load audio and waveform when track changes
|
||||
useEffect(() => {
|
||||
if (!track) {
|
||||
setIsPlaying(false)
|
||||
onPlayingChange(false)
|
||||
setCurrentTime(0)
|
||||
setWaveformPeaks([])
|
||||
return
|
||||
@@ -33,13 +34,13 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
||||
|
||||
if (audioRef.current) {
|
||||
audioRef.current.load()
|
||||
// Autoplay when track loads
|
||||
audioRef.current.play().then(() => {
|
||||
setIsPlaying(true)
|
||||
}).catch((error: unknown) => {
|
||||
console.error("Autoplay failed:", error)
|
||||
setIsPlaying(false)
|
||||
})
|
||||
// Autoplay when track loads if isPlaying is true
|
||||
if (isPlaying) {
|
||||
audioRef.current.play().catch((error: unknown) => {
|
||||
console.error("Autoplay failed:", error)
|
||||
onPlayingChange(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [track?.id])
|
||||
|
||||
@@ -54,7 +55,7 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
||||
setDuration(audio.duration)
|
||||
}
|
||||
}
|
||||
const handleEnded = () => setIsPlaying(false)
|
||||
const handleEnded = () => onPlayingChange(false)
|
||||
|
||||
audio.addEventListener("timeupdate", updateTime)
|
||||
audio.addEventListener("loadedmetadata", updateDuration)
|
||||
@@ -91,15 +92,24 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const togglePlay = () => {
|
||||
if (!audioRef.current || !track) return
|
||||
// Sync playing state with audio element
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current
|
||||
if (!audio) return
|
||||
|
||||
if (isPlaying) {
|
||||
audioRef.current.pause()
|
||||
audio.play().catch((error: unknown) => {
|
||||
console.error("Play failed:", error)
|
||||
onPlayingChange(false)
|
||||
})
|
||||
} else {
|
||||
audioRef.current.play()
|
||||
audio.pause()
|
||||
}
|
||||
setIsPlaying(!isPlaying)
|
||||
}, [isPlaying, onPlayingChange])
|
||||
|
||||
const togglePlay = () => {
|
||||
if (!audioRef.current || !track) return
|
||||
onPlayingChange(!isPlaying)
|
||||
}
|
||||
|
||||
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
||||
Reference in New Issue
Block a user