openfree commited on
Commit
7cce69a
·
verified ·
1 Parent(s): 4ce5e3d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -155
app.py CHANGED
@@ -4,14 +4,11 @@ import os
4
  import tempfile
5
  from dotenv import load_dotenv
6
 
7
- # 환경변수 로드
8
  load_dotenv()
9
-
10
- # OpenAI 클라이언트 설정
11
  api_key = os.getenv("OPENAI_API_KEY")
12
  if not api_key:
13
  print("⚠️ OPENAI_API_KEY를 .env 파일에 설정하세요!")
14
- print("예: OPENAI_API_KEY=sk-...")
15
  else:
16
  print(f"✅ API Key 로드됨: {api_key[:10]}...")
17
 
@@ -21,189 +18,181 @@ except Exception as e:
21
  print(f"❌ OpenAI 클라이언트 초기화 실패: {e}")
22
  client = None
23
 
24
-
 
 
25
  def translate_audio(audio_file, source_lang, target_lang):
26
- """음성 파일을 번역하는 함수"""
27
-
28
- # 입력 검증
29
  if not audio_file:
30
  return "⚠️ 오디오 파일을 업로드하거나 녹음하세요.", "", None
31
-
32
- if not api_key:
33
- return "❌ API 키가 설정되지 않았습니다. .env 파일을 확인하세요.", "", None
34
-
35
- if not client:
36
- return "❌ OpenAI 클라이언트가 초기화되지 않았습니다.", "", None
37
-
38
- # 같은 언어로 번역하려는 경우
39
  if source_lang == target_lang:
40
  return "⚠️ 입력 언어와 출력 언어가 같습니다.", "", None
41
 
42
  try:
43
- print(f"🎤 오디오 파일 처리 중: {audio_file}")
44
- print(f"📊 파일 크기: {os.path.getsize(audio_file) / 1024 / 1024:.2f} MB")
45
-
46
- # 1. Whisper로 음성을 텍스트로 변환
47
- print("1️⃣ 음성 인식 시작...")
48
  with open(audio_file, "rb") as f:
49
  transcript = client.audio.transcriptions.create(
50
  model="whisper-1",
51
  file=f,
52
  language=source_lang[:2].lower() if source_lang != "Chinese" else "zh"
53
  )
54
- original_text = transcript.text
55
- print(f"✅ 음성 인식 완료: {original_text[:50]}...")
 
56
 
57
- # 빈 텍스트 체크
58
- if not original_text.strip():
59
- return "⚠️ 음성이 인식되지 않았습니다. 다시 녹음해주세요.", "", None
60
-
61
- # 2. GPT-4로 번역
62
- print("2️⃣ 번역 시작...")
63
  response = client.chat.completions.create(
64
- model="gpt-3.5-turbo", # 더 빠르고 안정적
65
  messages=[
66
- {
67
- "role": "system",
68
- "content": f"You are a professional translator. Translate the following {source_lang} text to {target_lang}. Only provide the translation without any explanation or additional text."
69
- },
70
- {
71
- "role": "user",
72
- "content": original_text
73
- }
74
  ],
75
  temperature=0.3,
76
  max_tokens=2000
77
  )
78
  translated_text = response.choices[0].message.content.strip()
79
- print(f"✅ 번역 완료: {translated_text[:50]}...")
80
-
81
- # 3. TTS로 번역된 텍스트를 음성으로 변환
82
- print("3️⃣ 음성 합성 시작...")
83
-
84
- # 언어별 음성 선택
85
- voice_map = {
86
- "Korean": "nova",
87
- "English": "alloy",
88
- "Japanese": "nova",
89
- "Chinese": "nova",
90
- "Spanish": "nova",
91
- "French": "nova"
92
- }
93
- voice = voice_map.get(target_lang, "alloy")
94
 
 
 
95
  tts_response = client.audio.speech.create(
96
  model="tts-1",
97
- voice=voice,
98
- input=translated_text[:4096] # TTS 길이 제한
99
  )
 
 
 
 
 
100
 
101
- # 임시 파일로 저장
102
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
103
- tmp_file.write(tts_response.content)
104
- output_file = tmp_file.name
105
-
106
- print("✅ 모든 처리 완료!")
107
- return original_text, translated_text, output_file
108
-
109
- except openai.APIError as e:
110
- error_msg = f"❌ OpenAI API 오류: {str(e)}"
111
- print(error_msg)
112
- return error_msg, "", None
113
- except openai.AuthenticationError:
114
- error_msg = "❌ API 키가 올바르지 않습니다. .env 파일을 확인하세요."
115
- print(error_msg)
116
- return error_msg, "", None
117
- except openai.RateLimitError:
118
- error_msg = "❌ API 사용 한도를 초과했습니다. 잠시 후 다시 시도하세요."
119
- print(error_msg)
120
- return error_msg, "", None
121
  except Exception as e:
122
- error_msg = f"❌ 예상치 못한 오류: {type(e).__name__}: {str(e)}"
123
- print(error_msg)
124
- import traceback
125
- traceback.print_exc()
126
- return error_msg, "", None
127
-
128
-
129
- # Gradio 인터페이스
130
- with gr.Blocks(title="음성 번역기", theme=gr.themes.Soft()) as app:
131
- gr.Markdown(
132
- """
133
- # 🎙️ AI 음성 번역기
134
- 음성을 녹음하거나 업로드하면 자동으로 번역합니다.
135
-
136
- **지원 형식**: MP3, WAV, M4A, WEBM (최대 25MB)
137
- """
138
- )
139
-
140
- # API 키 상태 표시
141
- if api_key:
142
- gr.Markdown(f"✅ API 연결 상태: 정상 (키: {api_key[:10]}...)")
143
- else:
144
- gr.Markdown("❌ API 연결 상태: API 키를 설정하세요")
145
-
146
- with gr.Row():
147
- source_lang = gr.Dropdown(
148
- ["Korean", "English", "Japanese", "Chinese", "Spanish", "French"],
149
- value="Korean",
150
- label="입력 언어"
151
- )
152
- target_lang = gr.Dropdown(
153
- ["Korean", "English", "Japanese", "Chinese", "Spanish", "French"],
154
- value="English",
155
- label="출력 언어"
156
- )
157
 
158
- audio_input = gr.Audio(
159
- sources=["microphone", "upload"],
160
- type="filepath",
161
- label="음성 입력 (녹음 또는 파일 업로드)"
162
- )
163
 
164
- translate_btn = gr.Button("🔄 번역하기")
 
 
 
 
 
 
 
 
 
165
 
166
- with gr.Row():
167
- original_text = gr.Textbox(
168
- label="📝 원본 텍스트",
169
- lines=5,
170
- placeholder="음성 인식 결과가 여기에 표시됩니다..."
171
- )
172
- translated_text = gr.Textbox(
173
- label="🌐 번역된 텍스트",
174
- lines=5,
175
- placeholder="번역 결과가 여기에 표시됩니다..."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  )
 
 
177
 
178
- audio_output = gr.Audio(
179
- label="🔊 번역된 음성",
180
- type="filepath",
181
- autoplay=True
182
- )
183
-
184
- # 예시
185
- gr.Examples(
186
- examples=[
187
- ["Korean", "English"],
188
- ["English", "Korean"],
189
- ["Japanese", "English"],
190
- ["Chinese", "Korean"]
191
- ],
192
- inputs=[source_lang, target_lang],
193
- label="언어 조합 예시"
194
- )
195
-
196
- translate_btn.click(
197
- translate_audio,
198
- inputs=[audio_input, source_lang, target_lang],
199
- outputs=[original_text, translated_text, audio_output]
200
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
 
202
  if __name__ == "__main__":
203
  print("🚀 서버 시작 중...")
204
- app.launch(
205
- server_name="0.0.0.0",
206
- server_port=7860,
207
- share=False, # 로컬에서만 실행
208
- debug=True # 디버그 모드 활성화
209
- )
 
4
  import tempfile
5
  from dotenv import load_dotenv
6
 
7
+ # ===== 공통 초기화 =====
8
  load_dotenv()
 
 
9
  api_key = os.getenv("OPENAI_API_KEY")
10
  if not api_key:
11
  print("⚠️ OPENAI_API_KEY를 .env 파일에 설정하세요!")
 
12
  else:
13
  print(f"✅ API Key 로드됨: {api_key[:10]}...")
14
 
 
18
  print(f"❌ OpenAI 클라이언트 초기화 실패: {e}")
19
  client = None
20
 
21
+ # ----------------------------------------------------------
22
+ # (1) 기존: 음성(STT) → 번역 → 음성(TTS)
23
+ # ----------------------------------------------------------
24
  def translate_audio(audio_file, source_lang, target_lang):
 
 
 
25
  if not audio_file:
26
  return "⚠️ 오디오 파일을 업로드하거나 녹음하세요.", "", None
27
+ if not api_key or not client:
28
+ return "❌ API 초기화 오류", "", None
 
 
 
 
 
 
29
  if source_lang == target_lang:
30
  return "⚠️ 입력 언어와 출력 언어가 같습니다.", "", None
31
 
32
  try:
 
 
 
 
 
33
  with open(audio_file, "rb") as f:
34
  transcript = client.audio.transcriptions.create(
35
  model="whisper-1",
36
  file=f,
37
  language=source_lang[:2].lower() if source_lang != "Chinese" else "zh"
38
  )
39
+ original_text = transcript.text.strip()
40
+ if not original_text:
41
+ return "⚠️ 음성이 인식되지 않았습니다.", "", None
42
 
 
 
 
 
 
 
43
  response = client.chat.completions.create(
44
+ model="gpt-3.5-turbo",
45
  messages=[
46
+ {"role": "system",
47
+ "content": f"You are a professional translator. Translate the following {source_lang} text to {target_lang}. "
48
+ f"Only provide the translation without any explanation or additional text."},
49
+ {"role": "user", "content": original_text}
 
 
 
 
50
  ],
51
  temperature=0.3,
52
  max_tokens=2000
53
  )
54
  translated_text = response.choices[0].message.content.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
+ voice_map = {"Korean": "nova", "English": "alloy", "Japanese": "nova",
57
+ "Chinese": "nova", "Spanish": "nova", "French": "nova"}
58
  tts_response = client.audio.speech.create(
59
  model="tts-1",
60
+ voice=voice_map.get(target_lang, "alloy"),
61
+ input=translated_text[:4096]
62
  )
63
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
64
+ tmp.write(tts_response.content)
65
+ output_audio = tmp.name
66
+
67
+ return original_text, translated_text, output_audio
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  except Exception as e:
70
+ return f"❌ 오류: {type(e).__name__}: {str(e)}", "", None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
 
 
 
 
 
72
 
73
+ # ----------------------------------------------------------
74
+ # (2) 신규 탭: PDF / 이미지 → 번역 텍스트
75
+ # ----------------------------------------------------------
76
+ def translate_document(file_obj, source_lang, target_lang):
77
+ if not file_obj:
78
+ return "⚠️ 파일을 업로드하세요.", ""
79
+ if not api_key or not client:
80
+ return "❌ API 초기화 오류", ""
81
+ if source_lang == target_lang:
82
+ return "⚠️ 입력 언어와 출력 언어가 같습니다.", ""
83
 
84
+ ext = os.path.splitext(file_obj.name)[1].lower()
85
+ try:
86
+ # --- 원본 텍스트 추출 ---
87
+ if ext == ".pdf":
88
+ import pdfplumber
89
+ text_chunks = []
90
+ with pdfplumber.open(file_obj.name) as pdf:
91
+ for page in pdf.pages[:5]: # 데모: 앞 5쪽만
92
+ text_chunks.append(page.extract_text() or "")
93
+ original_text = "\n".join(text_chunks).strip()
94
+
95
+ elif ext in [".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tiff"]:
96
+ from PIL import Image
97
+ import pytesseract
98
+ original_text = pytesseract.image_to_string(Image.open(file_obj.name))
99
+
100
+ else:
101
+ return "⚠️ 지원하지 않는 형식입니다.", ""
102
+
103
+ if not original_text:
104
+ return "⚠️ 텍스트를 추출할 수 없습니다.", ""
105
+
106
+ # --- 번역 ---
107
+ response = client.chat.completions.create(
108
+ model="gpt-3.5-turbo",
109
+ messages=[
110
+ {"role": "system",
111
+ "content": f"You are a professional translator. Translate the following {source_lang} text to {target_lang}. "
112
+ f"Only provide the translation without any explanation or additional text."},
113
+ {"role": "user", "content": original_text}
114
+ ],
115
+ temperature=0.3,
116
+ max_tokens=4096
117
  )
118
+ translated_text = response.choices[0].message.content.strip()
119
+ return original_text, translated_text
120
 
121
+ except Exception as e:
122
+ return f" 오류: {type(e).__name__}: {str(e)}", ""
123
+
124
+
125
+ # ==========================================================
126
+ # Gradio UI (Tabs 구조)
127
+ # ==========================================================
128
+ with gr.Blocks(title="SMARTok Demo", theme=gr.themes.Soft()) as app:
129
+ with gr.Tabs():
130
+ # ----- ① 기존 음성 번역 -----
131
+ with gr.TabItem("🎙️ 음성 번역"):
132
+ gr.Markdown("""
133
+ # 🎙️ AI 음성 번역기
134
+ 마이크로 녹음하거나 오디오 파일을 업로드하면 **실시간 자막 + 번역 + 음성합성**까지 한 번에!
135
+ """)
136
+
137
+ with gr.Row():
138
+ src_lang_a = gr.Dropdown(
139
+ ["Korean", "English", "Japanese", "Chinese", "Spanish", "French"],
140
+ value="Korean", label="입력 언어"
141
+ )
142
+ tgt_lang_a = gr.Dropdown(
143
+ ["Korean", "English", "Japanese", "Chinese", "Spanish", "French"],
144
+ value="English", label="출력 언어"
145
+ )
146
+
147
+ audio_in = gr.Audio(
148
+ sources=["microphone", "upload"],
149
+ type="filepath",
150
+ label="음성 입력 (녹음 또는 파일 업로드)"
151
+ )
152
+ btn_audio = gr.Button("🔄 번역하기")
153
+
154
+ with gr.Row():
155
+ stt_text = gr.Textbox(label="📝 원본 텍스트", lines=5)
156
+ tlt_text = gr.Textbox(label="🌐 번역된 텍스트", lines=5)
157
+
158
+ audio_out = gr.Audio(label="🔊 번역된 음성", type="filepath", autoplay=True)
159
+
160
+ btn_audio.click(
161
+ translate_audio,
162
+ inputs=[audio_in, src_lang_a, tgt_lang_a],
163
+ outputs=[stt_text, tlt_text, audio_out]
164
+ )
165
+
166
+ # ----- ② 신규 자료 번역 -----
167
+ with gr.TabItem("📄 자료 번역"):
168
+ gr.Markdown("""
169
+ # 📄 PDF / 이미지 번역 데모
170
+ 교육자료·발표자료 등 **PDF 최대 5쪽** 또는 이미지 1장을 업로드하면 텍스트 추출 후 번역해줍니다.
171
+ """)
172
+
173
+ with gr.Row():
174
+ src_lang_d = gr.Dropdown(
175
+ ["Korean", "English", "Japanese", "Chinese", "Spanish", "French"],
176
+ value="Korean", label="입력 언어"
177
+ )
178
+ tgt_lang_d = gr.Dropdown(
179
+ ["Korean", "English", "Japanese", "Chinese", "Spanish", "French"],
180
+ value="English", label="출력 언어"
181
+ )
182
+
183
+ file_in = gr.File(label="PDF / 이미지 업로드")
184
+ btn_doc = gr.Button("🔄 번역하기")
185
+
186
+ original_doc = gr.Textbox(label="📝 추출된 원문", lines=15)
187
+ translated_doc = gr.Textbox(label="🌐 번역 결과", lines=15)
188
+
189
+ btn_doc.click(
190
+ translate_document,
191
+ inputs=[file_in, src_lang_d, tgt_lang_d],
192
+ outputs=[original_doc, translated_doc]
193
+ )
194
 
195
+ # ==========================================================
196
  if __name__ == "__main__":
197
  print("🚀 서버 시작 중...")
198
+ app.launch(server_name="0.0.0.0", server_port=7860, share=False, debug=True)