Spaces:
Sleeping
Sleeping
Enhance Dockerfile and Streamlit configuration; add upload directory, max upload size, and XSRF protection; update README with troubleshooting tips for file uploads
Browse files- .streamlit/config.toml +2 -0
- Dockerfile +4 -1
- README.md +16 -1
- src/streamlit_app.py +115 -23
.streamlit/config.toml
CHANGED
|
@@ -3,6 +3,8 @@ port = 8501
|
|
| 3 |
address = "0.0.0.0"
|
| 4 |
headless = true
|
| 5 |
enableCORS = false
|
|
|
|
|
|
|
| 6 |
|
| 7 |
[browser]
|
| 8 |
gatherUsageStats = false
|
|
|
|
| 3 |
address = "0.0.0.0"
|
| 4 |
headless = true
|
| 5 |
enableCORS = false
|
| 6 |
+
maxUploadSize = 200
|
| 7 |
+
enableXsrfProtection = false
|
| 8 |
|
| 9 |
[browser]
|
| 10 |
gatherUsageStats = false
|
Dockerfile
CHANGED
|
@@ -19,7 +19,8 @@ RUN apt-get update && \
|
|
| 19 |
&& rm -rf /var/lib/apt/lists/*
|
| 20 |
|
| 21 |
# Create necessary directories
|
| 22 |
-
RUN mkdir -p /app/tmp_model /tmp/matplotlib
|
|
|
|
| 23 |
|
| 24 |
# Copy requirements first (for better caching)
|
| 25 |
COPY requirements.txt .
|
|
@@ -39,6 +40,8 @@ RUN echo "[server]" > ./.streamlit/config.toml && \
|
|
| 39 |
echo "port = 8501" >> ./.streamlit/config.toml && \
|
| 40 |
echo "address = \"0.0.0.0\"" >> ./.streamlit/config.toml && \
|
| 41 |
echo "headless = true" >> ./.streamlit/config.toml && \
|
|
|
|
|
|
|
| 42 |
echo "" >> ./.streamlit/config.toml && \
|
| 43 |
echo "[browser]" >> ./.streamlit/config.toml && \
|
| 44 |
echo "gatherUsageStats = false" >> ./.streamlit/config.toml && \
|
|
|
|
| 19 |
&& rm -rf /var/lib/apt/lists/*
|
| 20 |
|
| 21 |
# Create necessary directories
|
| 22 |
+
RUN mkdir -p /app/tmp_model /tmp/matplotlib /app/uploads
|
| 23 |
+
RUN chmod -R 777 /app/uploads /app/tmp_model /tmp/matplotlib
|
| 24 |
|
| 25 |
# Copy requirements first (for better caching)
|
| 26 |
COPY requirements.txt .
|
|
|
|
| 40 |
echo "port = 8501" >> ./.streamlit/config.toml && \
|
| 41 |
echo "address = \"0.0.0.0\"" >> ./.streamlit/config.toml && \
|
| 42 |
echo "headless = true" >> ./.streamlit/config.toml && \
|
| 43 |
+
echo "maxUploadSize = 200" >> ./.streamlit/config.toml && \
|
| 44 |
+
echo "enableXsrfProtection = false" >> ./.streamlit/config.toml && \
|
| 45 |
echo "" >> ./.streamlit/config.toml && \
|
| 46 |
echo "[browser]" >> ./.streamlit/config.toml && \
|
| 47 |
echo "gatherUsageStats = false" >> ./.streamlit/config.toml && \
|
README.md
CHANGED
|
@@ -73,10 +73,25 @@ If you encounter errors like `Sign in to confirm you're not a bot` when using Yo
|
|
| 73 |
The app is containerized with Docker for easy deployment. Use the included Dockerfile to build and run:
|
| 74 |
|
| 75 |
```bash
|
|
|
|
| 76 |
docker build -t accent-detector .
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
```
|
| 79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
## Powered By
|
| 81 |
- [SpeechBrain](https://huggingface.co/speechbrain/lang-id-commonlanguage_ecapa)
|
| 82 |
- [Hugging Face Transformers](https://huggingface.co/speechbrain/lang-id-voxlingua107-ecapa)
|
|
|
|
| 73 |
The app is containerized with Docker for easy deployment. Use the included Dockerfile to build and run:
|
| 74 |
|
| 75 |
```bash
|
| 76 |
+
# Build the Docker image
|
| 77 |
docker build -t accent-detector .
|
| 78 |
+
|
| 79 |
+
# Run the container with volume mounting for better file handling
|
| 80 |
+
docker run -p 8501:8501 --volume /tmp/accent-detector:/app/uploads accent-detector
|
| 81 |
+
|
| 82 |
+
# For Windows users:
|
| 83 |
+
docker run -p 8501:8501 --volume C:\temp\accent-detector:/app/uploads accent-detector
|
| 84 |
```
|
| 85 |
|
| 86 |
+
### Troubleshooting Upload Issues
|
| 87 |
+
|
| 88 |
+
If you encounter 403 Forbidden errors when uploading files:
|
| 89 |
+
|
| 90 |
+
1. Make sure your audio file is under 200MB
|
| 91 |
+
2. Try converting your audio to a WAV or MP3 format
|
| 92 |
+
3. For longer files, consider extracting just the speech segment
|
| 93 |
+
4. If uploading an MP4 video, ensure it's not encrypted or DRM-protected
|
| 94 |
+
|
| 95 |
## Powered By
|
| 96 |
- [SpeechBrain](https://huggingface.co/speechbrain/lang-id-commonlanguage_ecapa)
|
| 97 |
- [Hugging Face Transformers](https://huggingface.co/speechbrain/lang-id-voxlingua107-ecapa)
|
src/streamlit_app.py
CHANGED
|
@@ -43,17 +43,23 @@ import matplotlib.pyplot as plt
|
|
| 43 |
import tempfile
|
| 44 |
import time
|
| 45 |
|
| 46 |
-
#
|
| 47 |
# To deploy this app:
|
| 48 |
# 1. Make sure Docker is installed
|
| 49 |
# 2. Build the Docker image: docker build -t accent-detector .
|
| 50 |
-
# 3. Run the container: docker run -p 8501:8501 accent-detector
|
|
|
|
| 51 |
# 4. Access the app at http://localhost:8501
|
| 52 |
#
|
| 53 |
# For cloud deployment:
|
| 54 |
# - Streamlit Cloud: Connect your GitHub repository to Streamlit Cloud
|
| 55 |
-
# - Hugging Face Spaces: Use the Docker deployment option
|
| 56 |
-
# - Azure/AWS/GCP: Deploy the container using their container services
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
# Load environment variables (if .env file exists)
|
| 59 |
try:
|
|
@@ -323,15 +329,52 @@ class AccentDetector:
|
|
| 323 |
|
| 324 |
def process_uploaded_audio(uploaded_file):
|
| 325 |
"""Process uploaded audio file"""
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
return results
|
| 336 |
|
| 337 |
# --- Streamlit App ---
|
|
@@ -479,9 +522,16 @@ with tab2:
|
|
| 479 |
st.markdown("### 🎵 Upload Audio File")
|
| 480 |
st.caption("**Recommended option!** Direct audio upload is more reliable than video URLs.")
|
| 481 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
uploaded_file = st.file_uploader("Upload an audio file",
|
| 483 |
-
type=["wav", "mp3", "m4a", "ogg", "flac"],
|
| 484 |
-
help="Support for WAV, MP3, M4A, OGG and
|
|
|
|
| 485 |
|
| 486 |
if uploaded_file is not None:
|
| 487 |
# Show a preview of the audio
|
|
@@ -494,14 +544,25 @@ with tab2:
|
|
| 494 |
analyze_button = st.button("Analyze Audio", type="primary", use_container_width=True)
|
| 495 |
with col2:
|
| 496 |
st.caption("Tip: 15-30 seconds of clear speech works best for accent detection")
|
| 497 |
-
|
| 498 |
if analyze_button:
|
| 499 |
with st.spinner("Analyzing audio... (this may take 15-30 seconds)"):
|
| 500 |
try:
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
#
|
| 504 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
|
| 506 |
# Create columns for results
|
| 507 |
col1, col2 = st.columns([2, 1])
|
|
@@ -511,8 +572,7 @@ with tab2:
|
|
| 511 |
st.markdown(f"**Detected Accent:** {results['accent']}")
|
| 512 |
st.markdown(f"**English Proficiency:** {results['english_confidence']:.1f}%")
|
| 513 |
st.markdown(f"**Accent Confidence:** {results['accent_confidence']:.1f}%")
|
| 514 |
-
|
| 515 |
-
# Show explanation in a box
|
| 516 |
st.markdown("### Expert Analysis")
|
| 517 |
st.info(results['explanation'])
|
| 518 |
|
|
@@ -520,8 +580,40 @@ with tab2:
|
|
| 520 |
if results['audio_viz']:
|
| 521 |
st.pyplot(results['audio_viz'])
|
| 522 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
except Exception as e:
|
| 524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
|
| 526 |
# Add footer with deployment info
|
| 527 |
st.markdown("---")
|
|
|
|
| 43 |
import tempfile
|
| 44 |
import time
|
| 45 |
|
| 46 |
+
# Deployment instructions:
|
| 47 |
# To deploy this app:
|
| 48 |
# 1. Make sure Docker is installed
|
| 49 |
# 2. Build the Docker image: docker build -t accent-detector .
|
| 50 |
+
# 3. Run the container: docker run -p 8501:8501 --volume /tmp/accent-detector:/app/uploads accent-detector
|
| 51 |
+
# For Windows: docker run -p 8501:8501 --volume C:\temp\accent-detector:/app/uploads accent-detector
|
| 52 |
# 4. Access the app at http://localhost:8501
|
| 53 |
#
|
| 54 |
# For cloud deployment:
|
| 55 |
# - Streamlit Cloud: Connect your GitHub repository to Streamlit Cloud
|
| 56 |
+
# - Hugging Face Spaces: Use the Docker deployment option with proper volume mounts
|
| 57 |
+
# - Azure/AWS/GCP: Deploy the container using their container services with persistent storage
|
| 58 |
+
#
|
| 59 |
+
# Troubleshooting file uploads:
|
| 60 |
+
# - Set maxUploadSize in .streamlit/config.toml
|
| 61 |
+
# - Ensure write permissions on upload directories
|
| 62 |
+
# - For 403 errors, check file size and format compatibility
|
| 63 |
|
| 64 |
# Load environment variables (if .env file exists)
|
| 65 |
try:
|
|
|
|
| 329 |
|
| 330 |
def process_uploaded_audio(uploaded_file):
|
| 331 |
"""Process uploaded audio file"""
|
| 332 |
+
try:
|
| 333 |
+
# Create a unique filename based on timestamp
|
| 334 |
+
timestamp = str(int(time.time()))
|
| 335 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
| 336 |
+
|
| 337 |
+
# Write the uploaded file to disk with proper extension
|
| 338 |
+
temp_input_path = f"uploaded_audio_{timestamp}{file_extension}"
|
| 339 |
+
with open(temp_input_path, "wb") as f:
|
| 340 |
+
f.write(uploaded_file.getbuffer())
|
| 341 |
+
|
| 342 |
+
# For MP4 files, extract the audio using ffmpeg
|
| 343 |
+
if file_extension == ".mp4":
|
| 344 |
+
st.info("Extracting audio from video file...")
|
| 345 |
+
audio_path = f"extracted_audio_{timestamp}.wav"
|
| 346 |
+
try:
|
| 347 |
+
subprocess.run(
|
| 348 |
+
['ffmpeg', '-i', temp_input_path, '-vn', '-acodec', 'pcm_s16le', '-ar', '16000', '-ac', '1', audio_path],
|
| 349 |
+
check=True,
|
| 350 |
+
capture_output=True
|
| 351 |
+
)
|
| 352 |
+
# Remove the original video file
|
| 353 |
+
os.remove(temp_input_path)
|
| 354 |
+
except subprocess.CalledProcessError as e:
|
| 355 |
+
st.error(f"Error extracting audio: {e}")
|
| 356 |
+
st.error(f"ffmpeg output: {e.stderr.decode('utf-8')}")
|
| 357 |
+
raise
|
| 358 |
+
else:
|
| 359 |
+
# For audio files, use them directly
|
| 360 |
+
audio_path = temp_input_path
|
| 361 |
+
|
| 362 |
+
detector = AccentDetector()
|
| 363 |
+
results = detector.analyze_audio(audio_path)
|
| 364 |
+
|
| 365 |
+
# Clean up
|
| 366 |
+
if os.path.exists(audio_path):
|
| 367 |
+
os.remove(audio_path)
|
| 368 |
+
|
| 369 |
+
return results
|
| 370 |
+
|
| 371 |
+
except Exception as e:
|
| 372 |
+
st.error(f"Error processing audio: {str(e)}")
|
| 373 |
+
if 'temp_input_path' in locals() and os.path.exists(temp_input_path):
|
| 374 |
+
os.remove(temp_input_path)
|
| 375 |
+
if 'audio_path' in locals() and os.path.exists(audio_path):
|
| 376 |
+
os.remove(audio_path)
|
| 377 |
+
raise
|
| 378 |
return results
|
| 379 |
|
| 380 |
# --- Streamlit App ---
|
|
|
|
| 522 |
st.markdown("### 🎵 Upload Audio File")
|
| 523 |
st.caption("**Recommended option!** Direct audio upload is more reliable than video URLs.")
|
| 524 |
|
| 525 |
+
# Add some information about file size limits
|
| 526 |
+
st.info("📝 **File Requirements**: \n"
|
| 527 |
+
"• Maximum file size: 200MB \n"
|
| 528 |
+
"• Supported formats: WAV, MP3, M4A, OGG, FLAC, MP4 \n"
|
| 529 |
+
"• Recommended length: 15-60 seconds of clear speech")
|
| 530 |
+
|
| 531 |
uploaded_file = st.file_uploader("Upload an audio file",
|
| 532 |
+
type=["wav", "mp3", "m4a", "ogg", "flac", "mp4"],
|
| 533 |
+
help="Support for WAV, MP3, M4A, OGG, FLAC and MP4 formats",
|
| 534 |
+
accept_multiple_files=False)
|
| 535 |
|
| 536 |
if uploaded_file is not None:
|
| 537 |
# Show a preview of the audio
|
|
|
|
| 544 |
analyze_button = st.button("Analyze Audio", type="primary", use_container_width=True)
|
| 545 |
with col2:
|
| 546 |
st.caption("Tip: 15-30 seconds of clear speech works best for accent detection")
|
|
|
|
| 547 |
if analyze_button:
|
| 548 |
with st.spinner("Analyzing audio... (this may take 15-30 seconds)"):
|
| 549 |
try:
|
| 550 |
+
# Check file size before processing
|
| 551 |
+
file_size_mb = len(uploaded_file.getvalue()) / (1024 * 1024)
|
| 552 |
+
if file_size_mb > 190: # Stay below the 200MB limit with some buffer
|
| 553 |
+
st.error(f"File size ({file_size_mb:.1f}MB) is too large. Maximum allowed is 190MB.")
|
| 554 |
+
st.info("Tip: Try trimming your audio to just the speech segment for better results.")
|
| 555 |
+
else:
|
| 556 |
+
# Check the file type and inform user about processing steps
|
| 557 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
| 558 |
+
if file_extension == '.mp4':
|
| 559 |
+
st.info("Processing video file - extracting audio track...")
|
| 560 |
+
|
| 561 |
+
# Process the file
|
| 562 |
+
results = process_uploaded_audio(uploaded_file)
|
| 563 |
+
|
| 564 |
+
# Display results
|
| 565 |
+
st.success("✅ Analysis Complete!")
|
| 566 |
|
| 567 |
# Create columns for results
|
| 568 |
col1, col2 = st.columns([2, 1])
|
|
|
|
| 572 |
st.markdown(f"**Detected Accent:** {results['accent']}")
|
| 573 |
st.markdown(f"**English Proficiency:** {results['english_confidence']:.1f}%")
|
| 574 |
st.markdown(f"**Accent Confidence:** {results['accent_confidence']:.1f}%")
|
| 575 |
+
# Show explanation in a box
|
|
|
|
| 576 |
st.markdown("### Expert Analysis")
|
| 577 |
st.info(results['explanation'])
|
| 578 |
|
|
|
|
| 580 |
if results['audio_viz']:
|
| 581 |
st.pyplot(results['audio_viz'])
|
| 582 |
|
| 583 |
+
except subprocess.CalledProcessError as e:
|
| 584 |
+
st.error("Error processing audio file")
|
| 585 |
+
st.error(f"FFmpeg error: {e.stderr.decode('utf-8') if e.stderr else str(e)}")
|
| 586 |
+
st.info("Troubleshooting tips:\n"
|
| 587 |
+
"• Try a different audio file format (WAV or MP3 recommended)\n"
|
| 588 |
+
"• Make sure the file is not corrupted\n"
|
| 589 |
+
"• Try a shorter audio clip")
|
| 590 |
+
|
| 591 |
+
except PermissionError as e:
|
| 592 |
+
st.error(f"Permission error: {str(e)}")
|
| 593 |
+
st.info("The app doesn't have permission to access or create temporary files. "
|
| 594 |
+
"This could be due to Docker container permissions. "
|
| 595 |
+
"Contact the administrator or try using a different file.")
|
| 596 |
+
|
| 597 |
+
except OSError as e:
|
| 598 |
+
st.error(f"System error: {str(e)}")
|
| 599 |
+
st.info("Check that the file isn't corrupted and try with a smaller audio clip.")
|
| 600 |
+
|
| 601 |
except Exception as e:
|
| 602 |
+
error_msg = str(e)
|
| 603 |
+
st.error(f"Error during analysis: {error_msg}")
|
| 604 |
+
|
| 605 |
+
if "403" in error_msg:
|
| 606 |
+
st.warning("Received a 403 Forbidden error. This may be due to: \n"
|
| 607 |
+
"• File size exceeding limits\n"
|
| 608 |
+
"• Temporary file permission issues\n"
|
| 609 |
+
"• Network restrictions")
|
| 610 |
+
st.info("Try a smaller audio file (less than 50MB) or a different format.")
|
| 611 |
+
elif "timeout" in error_msg.lower():
|
| 612 |
+
st.warning("The request timed out. Try a shorter audio clip or check your internet connection.")
|
| 613 |
+
elif "memory" in error_msg.lower():
|
| 614 |
+
st.warning("Out of memory error. Try a shorter audio clip.")
|
| 615 |
+
else:
|
| 616 |
+
st.info("If the problem persists, try a different audio file format such as MP3 or WAV.")
|
| 617 |
|
| 618 |
# Add footer with deployment info
|
| 619 |
st.markdown("---")
|