Spaces:
Sleeping
Sleeping
import os | |
# Fix permission errors for Streamlit | |
os.environ["XDG_CACHE_HOME"] = "/tmp/.cache" | |
os.environ["STREAMLIT_CONFIG_DIR"] = "/tmp/streamlit/config" | |
os.environ["STREAMLIT_CACHE_DIR"] = "/tmp/streamlit/cache" | |
os.environ["STREAMLIT_STATIC_DIR"] = "/tmp/streamlit/static" | |
os.environ["STREAMLIT_RUNTIME_DIR"] = "/tmp/streamlit/runtime" | |
import requests | |
import tempfile | |
import streamlit as st | |
from moviepy.editor import VideoFileClip | |
from speechbrain.inference.classifiers import EncoderClassifier | |
st.set_page_config(page_title="Accent Classifier", page_icon="π£οΈ", layout="centered") | |
# Custom CSS to change background color and style input box | |
st.markdown( | |
""" | |
<style> | |
.stApp { | |
background-color: #f0f0f0; | |
} | |
input[type="text"] { | |
background-color: white; | |
color: black; | |
border: 1px solid #ccc; | |
padding: 0.5rem; | |
border-radius: 5px; | |
} | |
.stTextInput > div > div > input { | |
background-color: white !important; | |
color: black !important; | |
border: 1px solid #ccc !important; | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
MODEL_ID = "Jzuluaga/accent-id-commonaccent_ecapa" | |
def download_video(url, output_path): | |
response = requests.get(url, stream=True) | |
if response.status_code == 200: | |
with open(output_path, "wb") as f: | |
for chunk in response.iter_content(1024): | |
f.write(chunk) | |
if not os.path.exists(output_path) or os.path.getsize(output_path) < 1024: | |
raise Exception("β Video download failed or file too small.") | |
else: | |
raise Exception("β Failed to download video.") | |
def extract_audio(video_path, audio_path): | |
clip = VideoFileClip(video_path) | |
audio = clip.audio | |
audio.write_audiofile(audio_path, fps=16000, nbytes=2, codec='pcm_s16le', ffmpeg_params=["-ac", "1"]) | |
clip.close() | |
audio.close() | |
def load_model(): | |
model_path = "/tmp/accent-id-model" # β switch from /data to /tmp | |
os.makedirs(model_path, exist_ok=True) | |
classifier = EncoderClassifier.from_hparams( | |
source=MODEL_ID, | |
savedir=model_path | |
) | |
return classifier | |
def classify_accent(audio_path, classifier): | |
audio_path_clean = os.path.abspath(audio_path).replace('\\', '/') | |
if not os.path.exists(audio_path_clean): | |
raise FileNotFoundError(f"Audio file not found: {audio_path_clean}") | |
out_prob, score, index, label = classifier.classify_file(audio_path_clean) | |
return label, round(score.item() * 100, 2) | |
# ---------------- UI ---------------- | |
st.title("π£οΈ Accent Classifier from Video") | |
st.markdown("Paste a direct **video URL (MP4)** and then press **Enter** or click **Identify the Accent**.") | |
with st.form("url_form", clear_on_submit=False): | |
video_url = st.text_input("π Video URL", placeholder="https://...") | |
submitted = st.form_submit_button("π£οΈ Identify the Accent") | |
if submitted: | |
if not video_url: | |
st.warning("β οΈ Please enter a video URL.") | |
else: | |
try: | |
if "dropbox.com" in video_url and "raw=1" not in video_url: | |
video_url = video_url.replace("dl=0", "raw=1").replace("?dl=0", "?raw=1") | |
with st.spinner("π Downloading and processing video..."): | |
with tempfile.TemporaryDirectory() as tmpdir: | |
video_path = os.path.join(tmpdir, "input_video.mp4") | |
audio_path = os.path.join(tmpdir, "output_audio.wav") | |
download_video(video_url, video_path) | |
extract_audio(video_path, audio_path) | |
classifier = load_model() | |
label, confidence = classify_accent(audio_path, classifier) | |
st.success("β Accent classified successfully!") | |
st.markdown(f"### π― Prediction: **{label}**") | |
st.markdown(f"π§ Confidence: **{confidence}%**") | |
st.info(f"The speaker's accent is predicted to be **{label}** with **{confidence}%** confidence.") | |
except Exception as e: | |
st.error(f"β Error: {e}") | |