openfree commited on
Commit
aba9e95
·
verified ·
1 Parent(s): e1fe24a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -573
app.py CHANGED
@@ -1,582 +1,35 @@
1
- # SMARTok Demo - 실시간 다국어 번역 시스템
2
- #
3
- # 필수 패키지:
4
- # pip install gradio openai python-dotenv pdfplumber numpy websockets
5
- #
6
- # 선택 패키지 (비디오 처리):
7
- # - ffmpeg 설치: sudo apt-get install ffmpeg (Linux) / brew install ffmpeg (Mac)
8
- # - 또는 pip install moviepy
9
- #
10
- # 환경 변수:
11
- # .env 파일에 OPENAI_API_KEY 설정 필요
12
 
13
- import os, asyncio, json, tempfile, websockets, pdfplumber
14
- import gradio as gr
15
- import openai
16
- from dotenv import load_dotenv
17
- import numpy as np
18
- import wave
19
- import subprocess
20
- import mimetypes
21
-
22
- # ─── 0. 초기화 ───────────────────────────────────────────────
23
- load_dotenv()
24
- openai.api_key = os.getenv("OPENAI_API_KEY")
25
- if not openai.api_key:
26
- raise RuntimeError("OPENAI_API_KEY 가 .env 에 없습니다!")
27
-
28
- # ffmpeg 설치 확인
29
- def check_ffmpeg():
30
- try:
31
- subprocess.run(['ffmpeg', '-version'], capture_output=True, check=True)
32
- return True
33
- except:
34
- return False
35
-
36
- HAS_FFMPEG = check_ffmpeg()
37
- if not HAS_FFMPEG:
38
- print("⚠️ ffmpeg가 설치되어 있지 않습니다. 비디오 처리가 제한될 수 있습니다.")
39
- print("설치 방법: sudo apt-get install ffmpeg (Linux) / brew install ffmpeg (Mac)")
40
-
41
- LANG = ["Korean","English","Japanese","Chinese",
42
- "Thai","Russian","Vietnamese","Spanish","French"]
43
- VOICE = {l: ("nova" if l in ["Korean","Japanese","Chinese"] else "alloy")
44
- for l in LANG}
45
- FOUR = ["English","Chinese","Thai","Russian"]
46
- WS_URL = "wss://api.openai.com/v1/realtime" # 올바른 엔드포인트로 수정
47
-
48
- # ─── 1. 공통 GPT 번역 / TTS ─────────────────────────────────
49
- # 전역 클라이언트 관리
50
- client = None
51
-
52
- def get_client():
53
- global client
54
- if client is None:
55
- client = openai.AsyncClient()
56
- return client
57
-
58
- async def gpt_translate(text, src, tgt):
59
- try:
60
- client = get_client()
61
- rsp = await client.chat.completions.create(
62
- model="gpt-3.5-turbo",
63
- messages=[{"role":"system",
64
- "content":f"Translate {src} → {tgt}. Return only the text."},
65
- {"role":"user","content":text}],
66
- temperature=0.3,max_tokens=2048)
67
- return rsp.choices[0].message.content.strip()
68
- except Exception as e:
69
- print(f"번역 오류: {e}")
70
- return ""
71
-
72
- async def gpt_tts(text, lang):
73
- try:
74
- client = get_client()
75
- rsp = await client.audio.speech.create(
76
- model="tts-1", voice=VOICE[lang], input=text[:4096])
77
- tmp = tempfile.NamedTemporaryFile(delete=False,suffix=".mp3")
78
- tmp.write(rsp.content); tmp.close(); return tmp.name
79
- except Exception as e:
80
- print(f"TTS 오류: {e}")
81
- return None
82
-
83
- # ─── 2. PDF 번역 ────────────────────────────────────────────
84
- def translate_pdf(file, src, tgt):
85
- if not file: return "⚠️ PDF 업로드 필요", ""
86
- with pdfplumber.open(file.name) as pdf:
87
- text = "\n".join(p.extract_text() or "" for p in pdf.pages[:5]).strip()
88
- if not text:
89
- return "⚠️ 텍스트 추출 실패", ""
90
- return text, asyncio.run(gpt_translate(text, src, tgt))
91
-
92
- # ─── 2-1. 오디오 번역 (탭1용) ────────────────────────────────
93
- def extract_audio_from_video(video_path):
94
- """MP4 등 비디오 파일에서 오디오 추출"""
95
- audio_output = None
96
- try:
97
- # 임시 오디오 파일 생성
98
- audio_output = tempfile.NamedTemporaryFile(delete=False, suffix=".wav")
99
- audio_output.close()
100
-
101
- # 방법 1: ffmpeg 사용 시도
102
- if HAS_FFMPEG:
103
- cmd = [
104
- 'ffmpeg',
105
- '-i', video_path,
106
- '-vn', # 비디오 스트림 제거
107
- '-acodec', 'pcm_s16le', # WAV 포맷
108
- '-ar', '16000', # 16kHz 샘플링
109
- '-ac', '1', # 모노
110
- '-y', # 덮어쓰기
111
- audio_output.name
112
- ]
113
-
114
- result = subprocess.run(cmd, capture_output=True, text=True)
115
-
116
- if result.returncode == 0:
117
- return audio_output.name
118
- else:
119
- print(f"ffmpeg 오류: {result.stderr}")
120
-
121
- # 방법 2: moviepy 사용 시도
122
- try:
123
- from moviepy.editor import VideoFileClip
124
- print("moviepy를 사용하여 오디오 추출 중...")
125
- video = VideoFileClip(video_path)
126
- video.audio.write_audiofile(
127
- audio_output.name,
128
- fps=16000,
129
- nbytes=2,
130
- codec='pcm_s16le',
131
- verbose=False,
132
- logger=None
133
- )
134
- video.close()
135
- return audio_output.name
136
- except ImportError:
137
- raise Exception(
138
- "비디오 처리를 위해 ffmpeg 또는 moviepy가 필요합니다.\n"
139
- "설치: pip install moviepy 또는 ffmpeg 설치"
140
- )
141
- except Exception as e:
142
- raise Exception(f"moviepy 오류: {str(e)}")
143
-
144
- except Exception as e:
145
- # 오류 시 임시 파일 정리
146
- if audio_output and os.path.exists(audio_output.name):
147
- os.unlink(audio_output.name)
148
- raise e
149
-
150
- async def translate_audio_async(file, src, tgt):
151
- if not file: return "⚠️ 오디오/비디오 업로드 필요", "", None
152
-
153
  try:
154
- # 파일 타입 확인
155
- mime_type, _ = mimetypes.guess_type(file)
156
- audio_file_path = file
157
- temp_audio_path = None
158
 
159
- # 비디오 파일인 경우 오디오 추출
160
- if mime_type and mime_type.startswith('video/'):
161
- print(f"비디오 파일 감지: {mime_type}")
162
- print(f"파일 크기: {os.path.getsize(file) / 1024 / 1024:.1f} MB")
163
- print("비디오에서 오디오 추출 중... (시간이 걸릴 수 있습니다)")
164
- temp_audio_path = extract_audio_from_video(file)
165
- audio_file_path = temp_audio_path
166
- print("오디오 추출 완료!")
167
 
168
- # STT: Whisper API 사용
169
- print("음성 인식 중...")
170
- client = get_client()
171
- with open(audio_file_path, 'rb') as audio_file:
172
- transcript = await client.audio.transcriptions.create(
173
- model="whisper-1",
174
- file=audio_file,
175
- language=src[:2].lower() # 언어 코드 간소화
176
- )
177
 
178
- # 임시 파일 정리
179
- if temp_audio_path and os.path.exists(temp_audio_path):
180
- os.unlink(temp_audio_path)
181
 
182
- orig_text = transcript.text
183
- if not orig_text.strip():
184
- return "⚠️ 음성이 감지되지 않았습니다", "", None
185
-
186
- print(f"인식된 텍스트: {orig_text[:50]}...")
187
-
188
- # 번역
189
- print(f"{src} → {tgt} 번역 중...")
190
- trans_text = await gpt_translate(orig_text, src, tgt)
191
-
192
- # TTS
193
- print("음성 합성 중...")
194
- audio_path = await gpt_tts(trans_text, tgt)
195
-
196
- return orig_text, trans_text, audio_path
197
- except Exception as e:
198
- print(f"오디오 번역 오류: {e}")
199
- # 임시 파일 정리
200
- if 'temp_audio_path' in locals() and temp_audio_path and os.path.exists(temp_audio_path):
201
- os.unlink(temp_audio_path)
202
-
203
- error_msg = str(e)
204
- if "ffmpeg" in error_msg.lower():
205
- error_msg += "\n\n💡 해결 방법:\n1. ffmpeg 설치: sudo apt-get install ffmpeg\n2. 또는 pip install moviepy"
206
-
207
- return "⚠️ 번역 중 오류 발생", error_msg, None
208
-
209
- def translate_audio(file, src, tgt):
210
- return asyncio.run(translate_audio_async(file, src, tgt))
211
-
212
- # ─── 3. 실시간 STT (Whisper API 사용) ──────────────────────────
213
- async def process_audio_chunk(audio_data, src_lang):
214
- """오디오 청크를 처리하여 텍스트로 변환"""
215
- if audio_data is None:
216
- return ""
217
-
218
- try:
219
- # Gradio는 (sample_rate, audio_array) 튜플을 반환
220
- if isinstance(audio_data, tuple):
221
- sample_rate, audio_array = audio_data
222
-
223
- # 오디오가 너무 짧으면 무시 (0.5초 미만)
224
- if len(audio_array) < sample_rate * 0.5:
225
- return ""
226
-
227
- # 오디오 정규화 및 노이즈 필터링
228
- audio_array = audio_array.astype(np.float32)
229
-
230
- # 무음 감지 - RMS가 너무 낮으면 무시
231
- rms = np.sqrt(np.mean(audio_array**2))
232
- if rms < 0.01: # 무음 임계값
233
- return ""
234
-
235
- # 정규화
236
- max_val = np.max(np.abs(audio_array))
237
- if max_val > 0:
238
- audio_array = audio_array / max_val * 0.95
239
-
240
- # numpy array를 WAV 파일로 변환
241
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
242
- with wave.open(tmp.name, 'wb') as wav_file:
243
- wav_file.setnchannels(1) # mono
244
- wav_file.setsampwidth(2) # 16-bit
245
- wav_file.setframerate(sample_rate)
246
-
247
- # float32를 16-bit PCM으로 변환
248
- audio_int16 = (audio_array * 32767).astype(np.int16)
249
- wav_file.writeframes(audio_int16.tobytes())
250
- tmp_path = tmp.name
251
- else:
252
- # bytes 데이터인 경우
253
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
254
- tmp.write(audio_data)
255
- tmp_path = tmp.name
256
-
257
- # Whisper API로 변환 - 언어 힌트와 프롬프트 추가
258
- with open(tmp_path, 'rb') as audio_file:
259
- # 언어별 프롬프트 설정으로 hallucination 방지
260
- language_prompts = {
261
- "Korean": "이것은 한국어 대화입니다.",
262
- "English": "This is an English conversation.",
263
- "Japanese": "これは日本語の会話です。",
264
- "Chinese": "这是中文对话。",
265
- }
266
-
267
- prompt = language_prompts.get(src_lang, "")
268
 
269
- client = get_client()
270
- transcript = await client.audio.transcriptions.create(
271
- model="whisper-1",
272
- file=audio_file,
273
- language=src_lang[:2].lower(),
274
- prompt=prompt,
275
- temperature=0.0 # 더 보수적인 추론
276
- )
277
-
278
- os.unlink(tmp_path) # 임시 파일 삭제
279
-
280
- # 결과 후처리 - 반복되는 패턴 제거
281
- text = transcript.text.strip()
282
-
283
- # 같은 문장이 반복되는 경우 처리
284
- sentences = text.split('.')
285
- if len(sentences) > 1:
286
- unique_sentences = []
287
- for sent in sentences:
288
- sent = sent.strip()
289
- if sent and (not unique_sentences or sent != unique_sentences[-1]):
290
- unique_sentences.append(sent)
291
- text = '. '.join(unique_sentences)
292
- if text and not text.endswith('.'):
293
- text += '.'
294
-
295
- # 뉴스 관련 hallucination 패턴 감지 및 제거
296
- hallucination_patterns = [
297
- "MBC 뉴스", "KBS 뉴스", "SBS 뉴스", "JTBC 뉴스",
298
- "뉴스룸", "뉴스데스크", "앵커", "기자입니다"
299
- ]
300
-
301
- # 짧은 텍스트에서 뉴스 패턴이 감지되면 무시
302
- if len(text) < 50 and any(pattern in text for pattern in hallucination_patterns):
303
- return ""
304
-
305
- return text
306
-
307
  except Exception as e:
308
- print(f"STT 오류: {e}")
309
- return ""
310
-
311
- # ─── 4. Gradio 스트림 핸들러 (동기 버전) ─────────────────────
312
- def realtime_single_sync(audio, src, tgt, state):
313
- """동기 버전의 실시간 단일 언어 번역"""
314
- if state is None:
315
- state = {"orig": "", "trans": "", "audio_buffer": [], "sample_rate": None}
316
-
317
- if audio is None:
318
- # 스트림 종료 시 남은 버퍼 처리
319
- if state["audio_buffer"] and state["sample_rate"]:
320
- try:
321
- # 버퍼의 오디오 합치기
322
- combined_audio = np.concatenate(state["audio_buffer"])
323
- audio_data = (state["sample_rate"], combined_audio)
324
-
325
- # 비동기 작업 실행
326
- text = asyncio.run(process_audio_chunk(audio_data, src))
327
- if text:
328
- state["orig"] = state["orig"] + " " + text if state["orig"] else text
329
- trans = asyncio.run(gpt_translate(text, src, tgt))
330
- state["trans"] = state["trans"] + " " + trans if state["trans"] else trans
331
- except Exception as e:
332
- print(f"처리 오류: {e}")
333
- state["audio_buffer"] = []
334
-
335
- return state["orig"], state["trans"], state
336
-
337
- # 오디오 데이터 버퍼링
338
- if isinstance(audio, tuple):
339
- sample_rate, audio_array = audio
340
- state["sample_rate"] = sample_rate
341
- state["audio_buffer"].append(audio_array)
342
-
343
- # 버퍼가 충분히 쌓였을 때만 처리 (약 2-3초 분량)
344
- if state["audio_buffer"]: # 버퍼가 비어있지 않은지 확인
345
- buffer_duration = len(np.concatenate(state["audio_buffer"])) / sample_rate
346
- if buffer_duration >= 2.0: # 2초마다 처리
347
- try:
348
- # 버퍼의 오디오 합치기
349
- combined_audio = np.concatenate(state["audio_buffer"])
350
- audio_data = (sample_rate, combined_audio)
351
-
352
- # STT
353
- text = asyncio.run(process_audio_chunk(audio_data, src))
354
- if text:
355
- state["orig"] = state["orig"] + " " + text if state["orig"] else text
356
-
357
- # 번역
358
- trans = asyncio.run(gpt_translate(text, src, tgt))
359
- state["trans"] = state["trans"] + " " + trans if state["trans"] else trans
360
-
361
- # 버퍼 초기화
362
- state["audio_buffer"] = []
363
- except Exception as e:
364
- print(f"처리 오류: {e}")
365
- state["audio_buffer"] = [] # 오류 시에도 버퍼 초기화
366
-
367
- return state["orig"], state["trans"], state
368
-
369
- def realtime_four_sync(audio, src, state):
370
- """동기 버전의 실시간 4언어 번역"""
371
- if state is None:
372
- state = {"orig": "", "English": "", "Chinese": "", "Thai": "", "Russian": "",
373
- "audio_buffer": [], "sample_rate": None}
374
-
375
- if audio is None:
376
- # 스트림 종료 시 남은 버퍼 처리
377
- if state["audio_buffer"] and state["sample_rate"]:
378
- try:
379
- combined_audio = np.concatenate(state["audio_buffer"])
380
- audio_data = (state["sample_rate"], combined_audio)
381
-
382
- text = asyncio.run(process_audio_chunk(audio_data, src))
383
- if text:
384
- state["orig"] = state["orig"] + " " + text if state["orig"] else text
385
-
386
- # 순차적으로 번역 (병렬 처리 시 문제 발생 가능)
387
- for lang in FOUR:
388
- trans = asyncio.run(gpt_translate(text, src, lang))
389
- state[lang] = state[lang] + " " + trans if state[lang] else trans
390
- except Exception as e:
391
- print(f"처리 오류: {e}")
392
- state["audio_buffer"] = []
393
-
394
- return (state["orig"], state["English"], state["Chinese"],
395
- state["Thai"], state["Russian"], state)
396
-
397
- # 오디오 데이터 버퍼링
398
- if isinstance(audio, tuple):
399
- sample_rate, audio_array = audio
400
- state["sample_rate"] = sample_rate
401
- state["audio_buffer"].append(audio_array)
402
-
403
- # 버퍼가 충분히 쌓였을 때만 처리
404
- if state["audio_buffer"]: # 버퍼가 비어있지 않은지 확인
405
- buffer_duration = len(np.concatenate(state["audio_buffer"])) / sample_rate
406
- if buffer_duration >= 2.0: # 2초마다 처리
407
- try:
408
- combined_audio = np.concatenate(state["audio_buffer"])
409
- audio_data = (sample_rate, combined_audio)
410
-
411
- # STT
412
- text = asyncio.run(process_audio_chunk(audio_data, src))
413
- if text:
414
- state["orig"] = state["orig"] + " " + text if state["orig"] else text
415
-
416
- # 4개 언어로 순차 번역
417
- for lang in FOUR:
418
- trans = asyncio.run(gpt_translate(text, src, lang))
419
- state[lang] = state[lang] + " " + trans if state[lang] else trans
420
-
421
- state["audio_buffer"] = []
422
- except Exception as e:
423
- print(f"처리 오류: {e}")
424
- state["audio_buffer"] = []
425
-
426
- return (state["orig"], state["English"], state["Chinese"],
427
- state["Thai"], state["Russian"], state)
428
-
429
- # ─── 5. UI ──────────────────────────────────────────────────
430
- with gr.Blocks(title="SMARTok Demo", theme=gr.themes.Soft()) as demo:
431
- gr.Markdown(
432
- """
433
- # 🌍 SMARTok 실시간 번역 시스템
434
-
435
- 다국어 실시간 번역을 지원하는 통합 번역 플랫폼
436
- """
437
- )
438
-
439
- with gr.Tabs():
440
- # 탭 1 – 오디오 번역
441
- with gr.TabItem("🎙️ 오디오/비디오"):
442
- gr.Markdown("### 🌐 오디오/비디오 파일 번역")
443
-
444
- with gr.Row():
445
- src1 = gr.Dropdown(LANG, value="Korean", label="입력 언어")
446
- tgt1 = gr.Dropdown(LANG, value="English", label="출력 언어")
447
-
448
- with gr.Tabs():
449
- with gr.TabItem("📁 파일 업로드"):
450
- # 파일 업로드 - 오디오와 비디오 모두 지원
451
- aud1_file = gr.File(
452
- label="오디오/비디오 파일 업로드",
453
- file_types=[".mp3", ".wav", ".m4a", ".flac", ".ogg", ".opus",
454
- ".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv"],
455
- type="filepath"
456
- )
457
- gr.Markdown(
458
- "📌 **지원 형식**\n"
459
- "- 오디오: MP3, WAV, M4A, FLAC, OGG, OPUS\n"
460
- "- 비디오: MP4, AVI, MOV, MKV, WebM, FLV\n\n"
461
- "⚠️ **주의사항**\n"
462
- "- 비디오 파일은 오디오 추출 시간이 필요합니다\n"
463
- "- 대용량 파일은 처리 시간이 오래 걸릴 수 있습니다"
464
- )
465
-
466
- with gr.TabItem("🎤 마이크 녹음"):
467
- aud1_mic = gr.Audio(
468
- sources=["microphone"],
469
- type="filepath",
470
- label="마이크 녹음"
471
- )
472
- gr.Markdown("💡 **팁**: 녹음 후 '정지' 버튼을 눌러주세요")
473
-
474
- btn1 = gr.Button("🔄 번역 시작", variant="primary", size="lg")
475
-
476
- # 진행 상태 표시
477
- status1 = gr.Textbox(label="진행 상태", value="대기 중...", interactive=False)
478
-
479
- with gr.Row():
480
- with gr.Column():
481
- o1 = gr.Textbox(label="📝 원문", lines=6)
482
- with gr.Column():
483
- t1 = gr.Textbox(label="📝 번역", lines=6)
484
-
485
- a1 = gr.Audio(label="🔊 번역된 음성 (TTS)", type="filepath", autoplay=True)
486
-
487
- # 파일이나 마이크 중 활성화된 입력 사용
488
- def translate_with_status(file_input, mic_input, src, tgt):
489
- active_input = file_input if file_input else mic_input
490
- if not active_input:
491
- return "⚠️ 파일을 업로드하거나 녹음을 해주세요", "", None
492
-
493
- # 상태 업데이트는 동기 함수에서 처리
494
- return translate_audio(active_input, src, tgt)
495
-
496
- btn1.click(
497
- lambda: "처리 중... 잠시만 기다려주세요 ⏳",
498
- outputs=status1
499
- ).then(
500
- translate_with_status,
501
- [aud1_file, aud1_mic, src1, tgt1],
502
- [o1, t1, a1]
503
- ).then(
504
- lambda: "✅ 완료!",
505
- outputs=status1
506
- )
507
-
508
- # 탭 2 – PDF 번역
509
- with gr.TabItem("📄 PDF"):
510
- src2 = gr.Dropdown(LANG, value="Korean", label="입력 언어")
511
- tgt2 = gr.Dropdown(LANG, value="English", label="출력 언어")
512
- pdf = gr.File(file_types=[".pdf"])
513
- btn2 = gr.Button("번역")
514
- o2 = gr.Textbox(label="추출 원문", lines=15)
515
- t2 = gr.Textbox(label="번역 결과", lines=15)
516
-
517
- btn2.click(translate_pdf, [pdf, src2, tgt2], [o2, t2])
518
-
519
- # 탭 3 – 실시간 1언어
520
- with gr.TabItem("⏱️ 실시간 1"):
521
- src3 = gr.Dropdown(LANG, value="Korean", label="입력 언어")
522
- tgt3 = gr.Dropdown(LANG, value="English", label="출력 언어")
523
-
524
- with gr.Row():
525
- with gr.Column():
526
- gr.Markdown("🎤 **마이크 입력**")
527
- mic3 = gr.Audio(
528
- sources=["microphone"],
529
- streaming=True,
530
- type="numpy", # numpy 형식 명시
531
- label="마이크"
532
- )
533
- gr.Markdown("💡 **사용 방법**\n- 2-3초 정도 문장을 말씀해주세요\n- 너무 짧거나 긴 문장은 인식이 어려울 수 있습니다")
534
-
535
- with gr.Column():
536
- o3 = gr.Textbox(label="원문(실시간)", lines=8, interactive=False)
537
- t3 = gr.Textbox(label="번역(실시간)", lines=8, interactive=False)
538
-
539
- st3 = gr.State()
540
-
541
- # stream 메서드 수정
542
- mic3.stream(
543
- realtime_single_sync,
544
- inputs=[mic3, src3, tgt3, st3],
545
- outputs=[o3, t3, st3],
546
- stream_every=0.5 # 0.5초마다 스트림 (time_limit 제거)
547
- )
548
-
549
- # 탭 4 – 실시간 4언어
550
- with gr.TabItem("🌏 실시간 4"):
551
- src4 = gr.Dropdown(LANG, value="Korean", label="입력 언어")
552
-
553
- with gr.Row():
554
- with gr.Column(scale=1):
555
- gr.Markdown("🎤 **마이크 입력**")
556
- mic4 = gr.Audio(
557
- sources=["microphone"],
558
- streaming=True,
559
- type="numpy",
560
- label="마이크"
561
- )
562
- o4 = gr.Textbox(label="원문", lines=8, interactive=False)
563
-
564
- with gr.Column(scale=2):
565
- with gr.Row():
566
- e4 = gr.Textbox(label="English", lines=8, interactive=False)
567
- c4 = gr.Textbox(label="Chinese(简体)", lines=8, interactive=False)
568
- with gr.Row():
569
- th4 = gr.Textbox(label="Thai", lines=8, interactive=False)
570
- r4 = gr.Textbox(label="Russian", lines=8, interactive=False)
571
-
572
- st4 = gr.State()
573
-
574
- # stream 메서드 수정
575
- mic4.stream(
576
- realtime_four_sync,
577
- inputs=[mic4, src4, st4],
578
- outputs=[o4, e4, c4, th4, r4, st4],
579
- stream_every=0.5
580
- )
581
 
582
- demo.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
 
1
+ import os
2
+ import sys
3
+ import streamlit as st
4
+ from tempfile import NamedTemporaryFile
 
 
 
 
 
 
 
5
 
6
+ def main():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  try:
8
+ # Get the code from secrets
9
+ code = os.environ.get("MAIN_CODE")
 
 
10
 
11
+ if not code:
12
+ st.error("⚠️ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
13
+ return
 
 
 
 
 
14
 
15
+ # Create a temporary Python file
16
+ with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
17
+ tmp.write(code)
18
+ tmp_path = tmp.name
 
 
 
 
 
19
 
20
+ # Execute the code
21
+ exec(compile(code, tmp_path, 'exec'), globals())
 
22
 
23
+ # Clean up the temporary file
24
+ try:
25
+ os.unlink(tmp_path)
26
+ except:
27
+ pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  except Exception as e:
30
+ st.error(f"⚠️ Error loading or executing the application: {str(e)}")
31
+ import traceback
32
+ st.code(traceback.format_exc())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ if __name__ == "__main__":
35
+ main()