Fix CORS
This commit is contained in:
@@ -5,7 +5,9 @@ POSTGRES_PASSWORD=audio_password
|
|||||||
POSTGRES_DB=audio_classifier
|
POSTGRES_DB=audio_classifier
|
||||||
|
|
||||||
# Backend API
|
# 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_HOST=0.0.0.0
|
||||||
API_PORT=8000
|
API_PORT=8000
|
||||||
|
|
||||||
@@ -16,4 +18,5 @@ ESSENTIA_MODELS_PATH=/app/models
|
|||||||
AUDIO_LIBRARY_PATH=/path/to/your/audio/library
|
AUDIO_LIBRARY_PATH=/path/to/your/audio/library
|
||||||
|
|
||||||
# Frontend
|
# 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"
|
DATABASE_URL: str = "postgresql://audio_user:audio_password@localhost:5432/audio_classifier"
|
||||||
|
|
||||||
# API Configuration
|
# 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_HOST: str = "0.0.0.0"
|
||||||
API_PORT: int = 8000
|
API_PORT: int = 8000
|
||||||
|
|
||||||
@@ -33,7 +34,13 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def cors_origins_list(self) -> List[str]:
|
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(",")]
|
return [origin.strip() for origin in self.CORS_ORIGINS.split(",")]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgresql://${POSTGRES_USER:-audio_user}:${POSTGRES_PASSWORD:-audio_password}@postgres:5432/${POSTGRES_DB:-audio_classifier}
|
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_USE_CLAP: ${ANALYSIS_USE_CLAP:-false}
|
||||||
ANALYSIS_NUM_WORKERS: ${ANALYSIS_NUM_WORKERS:-4}
|
ANALYSIS_NUM_WORKERS: ${ANALYSIS_NUM_WORKERS:-4}
|
||||||
ESSENTIA_MODELS_PATH: /app/models
|
ESSENTIA_MODELS_PATH: /app/models
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgresql://${POSTGRES_USER:-audio_user}:${POSTGRES_PASSWORD:-audio_password}@postgres:5432/${POSTGRES_DB:-audio_classifier}
|
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_USE_CLAP: ${ANALYSIS_USE_CLAP:-false}
|
||||||
ANALYSIS_NUM_WORKERS: ${ANALYSIS_NUM_WORKERS:-4}
|
ANALYSIS_NUM_WORKERS: ${ANALYSIS_NUM_WORKERS:-4}
|
||||||
ESSENTIA_MODELS_PATH: /app/models
|
ESSENTIA_MODELS_PATH: /app/models
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export default function Home() {
|
|||||||
const [filters, setFilters] = useState<FilterParams>({})
|
const [filters, setFilters] = useState<FilterParams>({})
|
||||||
const [page, setPage] = useState(0)
|
const [page, setPage] = useState(0)
|
||||||
const [currentTrack, setCurrentTrack] = useState<Track | null>(null)
|
const [currentTrack, setCurrentTrack] = useState<Track | null>(null)
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
const [searchQuery, setSearchQuery] = useState("")
|
const [searchQuery, setSearchQuery] = useState("")
|
||||||
const [isScanning, setIsScanning] = useState(false)
|
const [isScanning, setIsScanning] = useState(false)
|
||||||
const [scanStatus, setScanStatus] = useState<string>("")
|
const [scanStatus, setScanStatus] = useState<string>("")
|
||||||
@@ -233,10 +234,19 @@ export default function Home() {
|
|||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{/* Play button */}
|
{/* Play button */}
|
||||||
<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"
|
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">
|
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||||
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
|
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -347,7 +357,11 @@ export default function Home() {
|
|||||||
|
|
||||||
{/* Fixed Audio Player at bottom */}
|
{/* Fixed Audio Player at bottom */}
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-50">
|
<div className="fixed bottom-0 left-0 right-0 z-50">
|
||||||
<AudioPlayer track={currentTrack} />
|
<AudioPlayer
|
||||||
|
track={currentTrack}
|
||||||
|
isPlaying={isPlaying}
|
||||||
|
onPlayingChange={setIsPlaying}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import type { Track } from "@/lib/types"
|
|||||||
|
|
||||||
interface AudioPlayerProps {
|
interface AudioPlayerProps {
|
||||||
track: Track | null
|
track: Track | null
|
||||||
|
isPlaying: boolean
|
||||||
|
onPlayingChange: (playing: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AudioPlayer({ track }: AudioPlayerProps) {
|
export default function AudioPlayer({ track, isPlaying, onPlayingChange }: AudioPlayerProps) {
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
|
||||||
const [currentTime, setCurrentTime] = useState(0)
|
const [currentTime, setCurrentTime] = useState(0)
|
||||||
const [duration, setDuration] = useState(0)
|
const [duration, setDuration] = useState(0)
|
||||||
const [volume, setVolume] = useState(1)
|
const [volume, setVolume] = useState(1)
|
||||||
@@ -22,7 +23,7 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
|||||||
// Load audio and waveform when track changes
|
// Load audio and waveform when track changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!track) {
|
if (!track) {
|
||||||
setIsPlaying(false)
|
onPlayingChange(false)
|
||||||
setCurrentTime(0)
|
setCurrentTime(0)
|
||||||
setWaveformPeaks([])
|
setWaveformPeaks([])
|
||||||
return
|
return
|
||||||
@@ -33,14 +34,14 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
|||||||
|
|
||||||
if (audioRef.current) {
|
if (audioRef.current) {
|
||||||
audioRef.current.load()
|
audioRef.current.load()
|
||||||
// Autoplay when track loads
|
// Autoplay when track loads if isPlaying is true
|
||||||
audioRef.current.play().then(() => {
|
if (isPlaying) {
|
||||||
setIsPlaying(true)
|
audioRef.current.play().catch((error: unknown) => {
|
||||||
}).catch((error: unknown) => {
|
|
||||||
console.error("Autoplay failed:", error)
|
console.error("Autoplay failed:", error)
|
||||||
setIsPlaying(false)
|
onPlayingChange(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}, [track?.id])
|
}, [track?.id])
|
||||||
|
|
||||||
// Update current time as audio plays
|
// Update current time as audio plays
|
||||||
@@ -54,7 +55,7 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
|||||||
setDuration(audio.duration)
|
setDuration(audio.duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleEnded = () => setIsPlaying(false)
|
const handleEnded = () => onPlayingChange(false)
|
||||||
|
|
||||||
audio.addEventListener("timeupdate", updateTime)
|
audio.addEventListener("timeupdate", updateTime)
|
||||||
audio.addEventListener("loadedmetadata", updateDuration)
|
audio.addEventListener("loadedmetadata", updateDuration)
|
||||||
@@ -91,15 +92,24 @@ export default function AudioPlayer({ track }: AudioPlayerProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const togglePlay = () => {
|
// Sync playing state with audio element
|
||||||
if (!audioRef.current || !track) return
|
useEffect(() => {
|
||||||
|
const audio = audioRef.current
|
||||||
|
if (!audio) return
|
||||||
|
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
audioRef.current.pause()
|
audio.play().catch((error: unknown) => {
|
||||||
|
console.error("Play failed:", error)
|
||||||
|
onPlayingChange(false)
|
||||||
|
})
|
||||||
} else {
|
} 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>) => {
|
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user