boffire commited on
Commit
c75bf2b
ยท
verified ยท
1 Parent(s): 2c29c37

Update src/gradio_app.py

Browse files
Files changed (1) hide show
  1. src/gradio_app.py +144 -55
src/gradio_app.py CHANGED
@@ -2,23 +2,20 @@ import gradio as gr
2
  import os
3
  import requests
4
  import soundfile as sf
 
 
 
5
 
6
  # --- Configuration ---
7
  MAX_SIZE_MB = "50"
8
  MAX_SECONDS = 60
9
- # Replace with your actual key if your instance requires it
10
  LIBRE_API_KEY = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
11
  TRANSLATE_URL = "https://imsidag-community-libretranslate-kabyle.hf.space/translate"
12
 
13
  # --- Translation Logic ---
14
  def translate_to_english(text):
15
- """
16
- Sends the Kabyle transcript to a LibreTranslate instance.
17
- Expects application/x-www-form-urlencoded data.
18
- """
19
  if not text or any(symbol in text for symbol in ["โš ๏ธ", "โŒ"]):
20
  return ""
21
-
22
  payload = {
23
  'q': text,
24
  'source': 'kab',
@@ -26,9 +23,7 @@ def translate_to_english(text):
26
  'format': 'text',
27
  'api_key': LIBRE_API_KEY
28
  }
29
-
30
  try:
31
- # requests.post with 'data=' automatically uses x-www-form-urlencoded
32
  response = requests.post(TRANSLATE_URL, data=payload, timeout=15)
33
  response.raise_for_status()
34
  data = response.json()
@@ -36,12 +31,69 @@ def translate_to_english(text):
36
  except Exception as e:
37
  return f"โŒ Translation Error: {str(e)}"
38
 
39
- # --- Transcription & Processing Logic ---
40
- def process_audio(audio_file):
41
  """
42
- Handles the full pipeline: Validation -> Transcription -> Translation.
 
43
  """
44
- # 1. Validation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  if audio_file is None or (isinstance(audio_file, str) and audio_file.strip() == ""):
46
  return "โš ๏ธ Please upload an audio file first.", ""
47
 
@@ -53,19 +105,35 @@ def process_audio(audio_file):
53
  except Exception as e:
54
  return f"โŒ Error reading audio info: {str(e)}", ""
55
 
56
- # 2. Transcription
57
  try:
58
  from inference_file import inference
59
  transcript = inference(audio_file)
60
-
61
- # 3. Translation
62
  translation = translate_to_english(transcript)
63
-
64
  return transcript, translation
65
-
66
  except Exception as e:
67
  return f"โŒ Error during processing: {str(e)}", ""
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  # --- Build Gradio UI ---
71
  with gr.Blocks(title="๐ŸŽ™๏ธ Mmeslay") as demo:
@@ -74,51 +142,72 @@ with gr.Blocks(title="๐ŸŽ™๏ธ Mmeslay") as demo:
74
  # ๐ŸŽ™๏ธ Mmeslay by [G1ya777](https://github.com/G1ya777/Mmeslay)
75
  ### Kabyle ASR & Translation
76
  *Powered by Squeezeformer (ASR) and LibreTranslate (NMT)*
77
-
78
- Upload a Kabyle audio file or record directly to get a transcript and English translation.
79
  """
80
  )
81
 
82
- with gr.Row():
83
- with gr.Column(scale=1):
84
- audio_input = gr.Audio(
85
- label="Input Audio",
86
- type="filepath",
87
- sources=["upload", "microphone"],
88
- format="mp3",
89
- )
90
- transcribe_btn = gr.Button("๐Ÿš€ Transcribe & Translate", variant="primary", size="lg")
91
-
92
- with gr.Column(scale=2):
93
- text_output = gr.Textbox(
94
- label="Transcription (Kabyle)",
95
- lines=5
96
- )
97
- translation_output = gr.Textbox(
98
- label="LibreTranslate (English)",
99
- lines=5,
100
- placeholder="English LibreTranslate translation will appear here..."
101
- )
102
-
103
- # Map the button click to the dual-output function
104
- transcribe_btn.click(
105
- fn=process_audio,
106
- inputs=audio_input,
107
- outputs=[text_output, translation_output],
108
- )
109
 
110
- gr.Examples(
111
- examples=[
112
- "ressources/examples/e1.mp3",
113
- "ressources/examples/e2.mp3",
114
- ],
115
- inputs=audio_input,
116
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  gr.Markdown(
119
  """
120
  ---
121
- Developed by [G1ya777](https://github.com/G1ya777/Mmeslay).
122
  Examples from Tatoeba (CC BY licenses).
123
  """
124
  )
@@ -131,5 +220,5 @@ if __name__ == "__main__":
131
  server_name="0.0.0.0",
132
  server_port=port,
133
  max_file_size=f"{MAX_SIZE_MB}mb",
134
- theme=gr.themes.Soft(),
135
  )
 
2
  import os
3
  import requests
4
  import soundfile as sf
5
+ import tempfile
6
+ import re as regex
7
+ import subprocess
8
 
9
  # --- Configuration ---
10
  MAX_SIZE_MB = "50"
11
  MAX_SECONDS = 60
 
12
  LIBRE_API_KEY = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
13
  TRANSLATE_URL = "https://imsidag-community-libretranslate-kabyle.hf.space/translate"
14
 
15
  # --- Translation Logic ---
16
  def translate_to_english(text):
 
 
 
 
17
  if not text or any(symbol in text for symbol in ["โš ๏ธ", "โŒ"]):
18
  return ""
 
19
  payload = {
20
  'q': text,
21
  'source': 'kab',
 
23
  'format': 'text',
24
  'api_key': LIBRE_API_KEY
25
  }
 
26
  try:
 
27
  response = requests.post(TRANSLATE_URL, data=payload, timeout=15)
28
  response.raise_for_status()
29
  data = response.json()
 
31
  except Exception as e:
32
  return f"โŒ Translation Error: {str(e)}"
33
 
34
+ # --- YouTube Download Logic ---
35
+ def download_youtube_audio(url: str) -> str:
36
  """
37
+ Downloads audio from a YouTube URL using yt-dlp.
38
+ Returns the path to the downloaded audio file.
39
  """
40
+ if not url or not url.strip():
41
+ raise ValueError("Please provide a YouTube URL.")
42
+
43
+ # Basic URL validation
44
+ youtube_pattern = regex.compile(
45
+ r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
46
+ r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
47
+ )
48
+ if not youtube_pattern.match(url.strip()):
49
+ raise ValueError("Invalid YouTube URL.")
50
+
51
+ tmp_dir = tempfile.gettempdir()
52
+ output_template = os.path.join(tmp_dir, "youtube_%(id)s.%(ext)s")
53
+
54
+ # yt-dlp command: download best audio, convert to wav with ffmpeg
55
+ cmd = [
56
+ "yt-dlp",
57
+ "-f", "bestaudio/best",
58
+ "--extract-audio",
59
+ "--audio-format", "wav",
60
+ "--audio-quality", "0",
61
+ "--no-playlist",
62
+ "--max-filesize", f"{MAX_SIZE_MB}M",
63
+ "-o", output_template,
64
+ url.strip()
65
+ ]
66
+
67
+ result = subprocess.run(cmd, capture_output=True, text=True)
68
+ if result.returncode != 0:
69
+ raise RuntimeError(f"yt-dlp failed:\n{result.stderr}")
70
+
71
+ # Find the downloaded file
72
+ video_id = None
73
+ for line in result.stderr.splitlines() + result.stdout.splitlines():
74
+ m = regex.search(r'\[download\] Destination: .+youtube_([a-zA-Z0-9_-]+)\.wav', line)
75
+ if m:
76
+ video_id = m.group(1)
77
+ break
78
+
79
+ if not video_id:
80
+ # Fallback: glob the temp dir
81
+ import glob
82
+ files = glob.glob(os.path.join(tmp_dir, "youtube_*.wav"))
83
+ if not files:
84
+ raise FileNotFoundError("Download completed but audio file not found.")
85
+ audio_path = max(files, key=os.path.getctime)
86
+ else:
87
+ audio_path = os.path.join(tmp_dir, f"youtube_{video_id}.wav")
88
+
89
+ if not os.path.exists(audio_path):
90
+ raise FileNotFoundError("Downloaded audio file not found.")
91
+
92
+ return audio_path
93
+
94
+ # --- Unified Processing Logic ---
95
+ def process_audio(audio_file):
96
+ """Handles validation -> Transcription -> Translation."""
97
  if audio_file is None or (isinstance(audio_file, str) and audio_file.strip() == ""):
98
  return "โš ๏ธ Please upload an audio file first.", ""
99
 
 
105
  except Exception as e:
106
  return f"โŒ Error reading audio info: {str(e)}", ""
107
 
 
108
  try:
109
  from inference_file import inference
110
  transcript = inference(audio_file)
 
 
111
  translation = translate_to_english(transcript)
 
112
  return transcript, translation
 
113
  except Exception as e:
114
  return f"โŒ Error during processing: {str(e)}", ""
115
 
116
+ def process_youtube(url):
117
+ """Downloads YouTube audio then runs the ASR pipeline."""
118
+ if not url or not url.strip():
119
+ return "โš ๏ธ Please enter a YouTube URL.", ""
120
+
121
+ try:
122
+ audio_path = download_youtube_audio(url)
123
+ except Exception as e:
124
+ return f"โŒ Download Error: {str(e)}", ""
125
+
126
+ # Run the same pipeline
127
+ transcript, translation = process_audio(audio_path)
128
+
129
+ # Optional cleanup to save disk space
130
+ try:
131
+ if os.path.exists(audio_path):
132
+ os.remove(audio_path)
133
+ except Exception:
134
+ pass
135
+
136
+ return transcript, translation
137
 
138
  # --- Build Gradio UI ---
139
  with gr.Blocks(title="๐ŸŽ™๏ธ Mmeslay") as demo:
 
142
  # ๐ŸŽ™๏ธ Mmeslay by [G1ya777](https://github.com/G1ya777/Mmeslay)
143
  ### Kabyle ASR & Translation
144
  *Powered by Squeezeformer (ASR) and LibreTranslate (NMT)*
145
+
146
+ Upload a Kabyle audio file, record directly, **or paste a YouTube link** to get a transcript and English translation.
147
  """
148
  )
149
 
150
+ with gr.Tab("๐ŸŽง Audio Upload / Record"):
151
+ with gr.Row():
152
+ with gr.Column(scale=1):
153
+ audio_input = gr.Audio(
154
+ label="Input Audio",
155
+ type="filepath",
156
+ sources=["upload", "microphone"],
157
+ format="mp3",
158
+ )
159
+ transcribe_btn = gr.Button("๐Ÿš€ Transcribe & Translate", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ with gr.Column(scale=2):
162
+ text_output_1 = gr.Textbox(label="Transcription (Kabyle)", lines=5)
163
+ translation_output_1 = gr.Textbox(
164
+ label="LibreTranslate (English)",
165
+ lines=5,
166
+ placeholder="English LibreTranslate translation will appear here..."
167
+ )
168
+
169
+ transcribe_btn.click(
170
+ fn=process_audio,
171
+ inputs=audio_input,
172
+ outputs=[text_output_1, translation_output_1],
173
+ )
174
+
175
+ gr.Examples(
176
+ examples=[
177
+ "ressources/examples/e1.mp3",
178
+ "ressources/examples/e2.mp3",
179
+ ],
180
+ inputs=audio_input,
181
+ )
182
+
183
+ with gr.Tab("๐Ÿ“บ YouTube Link"):
184
+ with gr.Row():
185
+ with gr.Column(scale=1):
186
+ youtube_url = gr.Textbox(
187
+ label="YouTube URL",
188
+ placeholder="https://www.youtube.com/watch?v=...",
189
+ lines=1
190
+ )
191
+ youtube_btn = gr.Button("๐Ÿš€ Download & Transcribe", variant="primary", size="lg")
192
+
193
+ with gr.Column(scale=2):
194
+ text_output_2 = gr.Textbox(label="Transcription (Kabyle)", lines=5)
195
+ translation_output_2 = gr.Textbox(
196
+ label="LibreTranslate (English)",
197
+ lines=5,
198
+ placeholder="English LibreTranslate translation will appear here..."
199
+ )
200
+
201
+ youtube_btn.click(
202
+ fn=process_youtube,
203
+ inputs=youtube_url,
204
+ outputs=[text_output_2, translation_output_2],
205
+ )
206
 
207
  gr.Markdown(
208
  """
209
  ---
210
+ Developed by [G1ya777](https://github.com/G1ya777/Mmeslay).
211
  Examples from Tatoeba (CC BY licenses).
212
  """
213
  )
 
220
  server_name="0.0.0.0",
221
  server_port=port,
222
  max_file_size=f"{MAX_SIZE_MB}mb",
223
+ theme=gr.themes.Soft(),
224
  )