N4DerAX20 commited on
Commit
f75e191
·
verified ·
1 Parent(s): b181e94

Upload streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +143 -1
streamlit_app.py CHANGED
@@ -1,2 +1,144 @@
1
 
2
- <actual code from previous cell, omitted here for brevity>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
+ import streamlit as st
3
+ import tempfile
4
+ from faster_whisper import WhisperModel
5
+ import textwrap
6
+ from datetime import timedelta
7
+ from xml.sax.saxutils import escape
8
+
9
+ st.set_page_config(page_title="VO to Subtitle Generator Multi-languages version", layout="wide")
10
+ st.title("🌍 VO to Subtitle Generator — Multi-languages Version")
11
+
12
+ def format_time(seconds):
13
+ td = timedelta(seconds=seconds)
14
+ result = str(td)[:11].replace(".", ",")
15
+ return result if "," in result else result + ",000"
16
+
17
+ def generate_srt(segments, max_chars, max_lines):
18
+ srt_text = ""
19
+ count = 1
20
+ for seg in segments:
21
+ start = seg.start
22
+ end = seg.end
23
+ text = seg.text.strip()
24
+ lines = textwrap.wrap(text, width=max_chars)
25
+ grouped = [lines[i:i+max_lines] for i in range(0, len(lines), max_lines)]
26
+ chunk_count = len(grouped)
27
+ duration = end - start
28
+ chunk_duration = duration / chunk_count if chunk_count > 0 else duration
29
+
30
+ for j, chunk in enumerate(grouped):
31
+ chunk_start = start + j * chunk_duration
32
+ chunk_end = chunk_start + chunk_duration
33
+ timestamp = f"{format_time(chunk_start)} --> {format_time(chunk_end)}"
34
+ content = "\n".join(chunk)
35
+ srt_text += f"{count}\n{timestamp}\n{content}\n\n"
36
+ count += 1
37
+ return srt_text
38
+
39
+ def generate_fcpxml(segments, version):
40
+ xml = [f'<?xml version="1.0" encoding="UTF-8"?>',
41
+ f'<!DOCTYPE fcpxml>',
42
+ f'<fcpxml version="{version}">',
43
+ ' <resources>',
44
+ ' <format id="r1" name="FFVideoFormat1080p30" frameDuration="100/3000s" width="1920" height="1080"/>',
45
+ ' <effect id="r2" name="Basic Title" uid=".../Effects.localized/Basic Titles.localized/Basic Title.localized/Basic Title.moti"/>',
46
+ ' </resources>',
47
+ ' <library>',
48
+ ' <event name="Transcription">',
49
+ ' <project name="Subtitles">',
50
+ f' <sequence duration="{round(segments[-1].end, 2)}s" format="r1" tcStart="0s" tcFormat="NDF">',
51
+ ' <spine>']
52
+ for seg in segments:
53
+ start = round(seg.start, 2)
54
+ duration = round(seg.end - seg.start, 2)
55
+ text = escape(seg.text.strip())
56
+ xml.append(
57
+ f' <title lane="1" offset="{start}s" ref="r2" duration="{duration}s" start="{start}s">'
58
+ f' <text>{text}</text>'
59
+ f' </title>'
60
+ )
61
+ xml.extend([' </spine>',
62
+ ' </sequence>',
63
+ ' </project>',
64
+ ' </event>',
65
+ ' </library>',
66
+ '</fcpxml>'])
67
+ return '\n'.join(xml)
68
+
69
+ with st.sidebar:
70
+ st.header("⚙️ Settings")
71
+ uploaded_file = st.file_uploader("Upload MP3 or WAV", type=["mp3", "wav"])
72
+ model_size = st.selectbox("Model Size", ["tiny", "base", "small", "medium"])
73
+ layout = st.selectbox("Video Layout", ["Horizontal (37 chars)", "Vertical (25 chars)"])
74
+ lines = st.selectbox("Lines per Subtitle", [1, 2], index=1)
75
+ language_map = {
76
+ "Auto": None,
77
+ "Arabic": "ar",
78
+ "English": "en",
79
+ "French": "fr",
80
+ "Farsi": "fa",
81
+ "Spanish": "es"
82
+ }
83
+ language = st.selectbox("Language", list(language_map.keys()))
84
+ export_format = st.selectbox("Export Format", ["srt", "fcpxml"])
85
+ fcpxml_version = st.selectbox("FCPXML Version", ["1.13", "1.12", "1.11"], index=0) if export_format == "fcpxml" else None
86
+
87
+ if 'subtitle_data' not in st.session_state:
88
+ st.session_state.subtitle_data = ""
89
+ st.session_state.text_dir = "rtl"
90
+ st.session_state.generated = False
91
+
92
+ if uploaded_file and st.button("🔁 Generate Subtitle"):
93
+ with st.spinner("Transcribing with Whisper..."):
94
+ with tempfile.NamedTemporaryFile(delete=False) as temp_audio:
95
+ temp_audio.write(uploaded_file.read())
96
+ temp_audio.flush()
97
+ whisper = WhisperModel(model_size, device="cpu", compute_type="int8")
98
+ segments_gen, _ = whisper.transcribe(temp_audio.name, language=language_map[language])
99
+ segments = list(segments_gen)
100
+ st.session_state.segments = segments
101
+
102
+ max_chars = 25 if "Vertical" in layout else 37
103
+ max_lines = int(lines)
104
+
105
+ if export_format == "srt":
106
+ st.session_state.subtitle_data = generate_srt(segments, max_chars, max_lines)
107
+ else:
108
+ st.session_state.subtitle_data = generate_fcpxml(segments, fcpxml_version)
109
+
110
+ st.session_state.generated = True
111
+
112
+ if st.session_state.generated and st.session_state.subtitle_data:
113
+ col1, col2 = st.columns([1, 6])
114
+ with col1:
115
+ st.write("Text Direction:")
116
+ if st.button("⬅️ RTL"):
117
+ st.session_state.text_dir = "rtl"
118
+ if st.button("➡️ LTR"):
119
+ st.session_state.text_dir = "ltr"
120
+
121
+ with col2:
122
+ st.markdown("### ✏️ Edit Before Download")
123
+ preview_html = f'''
124
+ <textarea id="subtitle_editor" name="subtitle_editor"
125
+ style="width: 100%; height: 300px; padding: 1.5em 2em;
126
+ border: 1px solid #ccc; border-radius: 8px;
127
+ font-family: monospace; font-size: 14px;
128
+ direction: {st.session_state.text_dir}; white-space: pre-wrap;">{st.session_state.subtitle_data}</textarea>
129
+ <script>
130
+ const editor = document.getElementById('subtitle_editor');
131
+ editor.addEventListener('input', () => {{
132
+ window.subtitleEdited = editor.value;
133
+ }});
134
+ window.subtitleEdited = editor.value;
135
+ </script>
136
+ '''
137
+ st.components.v1.html(preview_html, height=360)
138
+
139
+ st.download_button(
140
+ label="⬇️ Download Subtitle",
141
+ data=st.session_state.subtitle_data,
142
+ file_name="subtitles." + ("srt" if export_format == "srt" else "fcpxml"),
143
+ mime="text/plain"
144
+ )