Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- README.md +37 -11
- requirements.txt +3 -0
- streamlit_app.py +53 -0
README.md
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
pinned: false
|
| 10 |
-
license: mit
|
| 11 |
-
short_description: Arabic Transcription to SRT
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
# ποΈ Arabic VO to Subtitle Generator (.srt / .fcpxmld)
|
| 2 |
+
|
| 3 |
+
This app takes an Arabic voiceover audio file (MP3 or WAV) and automatically transcribes it using OpenAI Whisper, producing subtitles in SRT format or FCPXMLD format for Final Cut Pro X.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## π Features
|
| 8 |
+
|
| 9 |
+
- π§ Transcription using Whisper (via Faster-Whisper for speed)
|
| 10 |
+
- π Outputs `.srt` subtitle files for editing and broadcast
|
| 11 |
+
- π¬ Also exports `.fcpxmld` for direct use in Final Cut Pro X
|
| 12 |
+
- π Custom options for vertical or horizontal layout
|
| 13 |
+
- π Supports Arabic (RTL) and other languages
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## π How to Use
|
| 18 |
+
|
| 19 |
+
1. Upload your Arabic MP3/WAV voiceover
|
| 20 |
+
2. Choose:
|
| 21 |
+
- Layout: Vertical (mobile) or Horizontal (TV)
|
| 22 |
+
- Lines per subtitle: 1 or 2
|
| 23 |
+
- Export format: `.srt` or `.fcpxmld`
|
| 24 |
+
3. Click **Transcribe**
|
| 25 |
+
4. Preview subtitles in the browser
|
| 26 |
+
5. Download the final file
|
| 27 |
+
|
| 28 |
---
|
| 29 |
+
|
| 30 |
+
## π₯οΈ Powered By
|
| 31 |
+
|
| 32 |
+
- [Faster Whisper](https://github.com/guillaumekln/faster-whisper)
|
| 33 |
+
- [Streamlit](https://streamlit.io)
|
| 34 |
+
- [Hugging Face Spaces](https://huggingface.co/spaces)
|
| 35 |
+
|
|
|
|
|
|
|
|
|
|
| 36 |
---
|
| 37 |
|
| 38 |
+
## π License
|
| 39 |
+
|
| 40 |
+
MIT β use it freely, credit appreciated!
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
faster-whisper
|
| 3 |
+
torch
|
streamlit_app.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import streamlit as st
|
| 3 |
+
import tempfile
|
| 4 |
+
from faster_whisper import WhisperModel
|
| 5 |
+
import textwrap
|
| 6 |
+
from datetime import timedelta
|
| 7 |
+
|
| 8 |
+
st.title("ποΈ Arabic VO to Subtitle Generator (.srt)")
|
| 9 |
+
|
| 10 |
+
uploaded_file = st.file_uploader("Upload Arabic MP3 or WAV", type=["mp3", "wav"])
|
| 11 |
+
model_size = st.selectbox("Model Size", ["tiny", "base", "small", "medium"], index=3)
|
| 12 |
+
layout = st.selectbox("Video Layout", ["Horizontal (37 chars)", "Vertical (25 chars)"])
|
| 13 |
+
lines = st.selectbox("Lines per Subtitle", [1, 2], index=1)
|
| 14 |
+
|
| 15 |
+
def format_time(seconds):
|
| 16 |
+
td = timedelta(seconds=seconds)
|
| 17 |
+
result = str(td)[:11].replace(".", ",")
|
| 18 |
+
return result if "," in result else result + ",000"
|
| 19 |
+
|
| 20 |
+
if uploaded_file:
|
| 21 |
+
with st.spinner("Transcribing with Whisper..."):
|
| 22 |
+
with tempfile.NamedTemporaryFile(delete=False) as temp_audio:
|
| 23 |
+
temp_audio.write(uploaded_file.read())
|
| 24 |
+
temp_audio.flush()
|
| 25 |
+
|
| 26 |
+
whisper = WhisperModel(model_size, device="cpu", compute_type="int8")
|
| 27 |
+
segments, _ = whisper.transcribe(temp_audio.name, language="ar")
|
| 28 |
+
|
| 29 |
+
max_chars = 25 if "Vertical" in layout else 37
|
| 30 |
+
max_lines = int(lines)
|
| 31 |
+
|
| 32 |
+
srt_text = ""
|
| 33 |
+
count = 1
|
| 34 |
+
for seg in segments:
|
| 35 |
+
start = seg.start
|
| 36 |
+
end = seg.end
|
| 37 |
+
text = seg.text.strip()
|
| 38 |
+
lines = textwrap.wrap(text, width=max_chars)
|
| 39 |
+
grouped = [lines[i:i+max_lines] for i in range(0, len(lines), max_lines)]
|
| 40 |
+
chunk_count = len(grouped)
|
| 41 |
+
duration = end - start
|
| 42 |
+
chunk_duration = duration / chunk_count if chunk_count > 0 else duration
|
| 43 |
+
|
| 44 |
+
for j, chunk in enumerate(grouped):
|
| 45 |
+
chunk_start = start + j * chunk_duration
|
| 46 |
+
chunk_end = chunk_start + chunk_duration
|
| 47 |
+
timestamp = f"{format_time(chunk_start)} --> {format_time(chunk_end)}"
|
| 48 |
+
content = "\n".join(chunk)
|
| 49 |
+
srt_text += f"{count}\n{timestamp}\n{content}\n\n"
|
| 50 |
+
count += 1
|
| 51 |
+
|
| 52 |
+
st.text_area("Preview SRT", value=srt_text, height=300)
|
| 53 |
+
st.download_button("β¬οΈ Download .srt", srt_text, file_name="output.srt", mime="text/plain")
|