AIRider commited on
Commit
5870494
ยท
verified ยท
1 Parent(s): 754ff35

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -206
app.py CHANGED
@@ -1,212 +1,71 @@
1
- import gradio as gr
2
- from gradio_client import Client
3
- import json
4
- import logging
5
- import openai
6
  import os
 
 
 
7
  import re
8
- import html
9
-
10
- # ๋กœ๊น… ์„ค์ •
11
- logging.basicConfig(filename='youtube_script_extractor.log', level=logging.DEBUG,
12
- format='%(asctime)s - %(levelname)s - %(message)s')
13
-
14
- openai.api_key = os.getenv("OPENAI_API_KEY")
15
-
16
- def parse_api_response(response):
17
- try:
18
- if isinstance(response, str):
19
- response = json.loads(response)
20
- if isinstance(response, list) and len(response) > 0:
21
- response = response[0]
22
- if not isinstance(response, dict):
23
- raise ValueError(f"์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์‘๋‹ต ํ˜•์‹์ž…๋‹ˆ๋‹ค. ๋ฐ›์€ ๋ฐ์ดํ„ฐ ํƒ€์ž…: {type(response)}")
24
- return response
25
- except Exception as e:
26
- logging.error(f"API ์‘๋‹ต ํŒŒ์‹ฑ ์‹คํŒจ: {str(e)}")
27
- raise ValueError(f"API ์‘๋‹ต ํŒŒ์‹ฑ ์‹คํŒจ: {str(e)}")
28
-
29
- def get_youtube_script(url):
30
- logging.info(f"์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ ์‹œ์ž‘: URL = {url}")
31
- client = Client("whispersound/YT_Ts_R")
32
- try:
33
- result = client.predict(youtube_url=url, api_name="/predict")
34
- parsed_result = parse_api_response(result)
35
-
36
- if 'data' not in parsed_result or not parsed_result['data']:
37
- raise ValueError("API ์‘๋‹ต์— ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
38
-
39
- data = parsed_result["data"][0]
40
- title = data.get("title", "์ œ๋ชฉ ์—†์Œ")
41
- description = data.get("description", "์„ค๋ช… ์—†์Œ")
42
- transcription_text = data.get("transcriptionAsText", "")
43
- thumbnails = data.get("thumbnails", [])
44
-
45
- if not transcription_text:
46
- raise ValueError("์ถ”์ถœ๋œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
47
-
48
- logging.info("์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ ์™„๋ฃŒ")
49
- return title, description, transcription_text, thumbnails
50
- except Exception as e:
51
- logging.exception("์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ")
52
- raise
53
-
54
- def call_api(prompt, max_tokens, temperature, top_p):
55
- try:
56
- response = openai.ChatCompletion.create(
57
- model="gpt-4o-mini",
58
- messages=[{"role": "user", "content": prompt}],
59
- max_tokens=max_tokens,
60
- temperature=temperature,
61
- top_p=top_p
62
- )
63
- return response['choices'][0]['message']['content']
64
- except Exception as e:
65
- logging.exception("LLM API ํ˜ธ์ถœ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ")
66
- raise
67
-
68
- def summarize_text(title, description, text):
69
- prompt = f"""
70
- [์œ ํŠœ๋ธŒ ์š”์•ฝ ๊ทœ์น™]
71
- 1. ๋„ˆ๋Š” ์œ ํŠœ๋ธŒ ์˜์ƒ ์ „๋ฌธ ํ•ด์„ค๊ฐ€๋กœ์„œ ์ง€์นจ์— ๋งž๊ฒŒ ์ด ๊ธ€์„ ์ž‘์„ฑํ•˜๋ผ
72
- 2. ์•„๋ž˜์˜ ์ œ๋ชฉ๊ณผ ์„ค๋ช…์€ ์ด ์œ ํŠœ๋ธŒ ์˜์ƒ์˜ ์›๋ณธ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์ด๋‹ค.
73
- 3. ๋ฐ˜๋“œ์‹œ ์ œ๋ชฉ๊ณผ ์„ค๋ช…์œผ๋กœ ์ฃผ์ œ์™€ ๋ฌธ๋งฅ, ์ฒ ์ž(Spelling)์„ ๋จผ์ € ํŒŒ์•…ํ•˜๊ณ , ์•„๋ž˜์˜ ๋Œ€๋ณธ์„ ๋ฐ˜๋“œ์‹œ ์ง€์นจ์— ๋งž๊ฒŒ ์ƒ์„ธํ•˜๊ฒŒ ์š”์•ฝํ•˜๋ผ
74
- - ๋ฐ˜๋“œ์‹œ ์ฃผ์–ด์ง„ ์ œ๋ชฉ, ์„ค๋ช…์— ์žˆ๋Š” ์ฒ ์ž(Spelling)๋ฅผ ์š”์•ฝ์— ๋ฐ˜์˜ํ•˜๋ผ(์›๋ฌธ ๋Œ€๋ณธ์—๋Š” ์˜คํƒˆ์ž๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค)
75
- 4. ๋ฐ˜๋“œ์‹œ ํ•œ๊ธ€๋กœ ์ž‘์„ฑํ•˜๋ผ
76
- 5. ๋ฐ˜๋“œ์‹œ '์ด ์œ ํŠœ๋ธŒ ๋Œ€๋ณธ์€', '์ด ์˜์ƒ์€', '์ด ์œ ํŠœ๋ธŒ๋Š”'๋“ฑ์˜ ์†Œ๊ฐœ์‹ ํ‘œํ˜„์€ ์ œ์™ธํ•˜๋ผ
77
- 6. ์š”์•ฝ๋ฌธ๋งŒ์œผ๋กœ๋„ ์˜์ƒ์„ ์ง์ ‘ ์‹œ์ฒญํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ์ˆ˜์ค€์œผ๋กœ ๋‚ด์šฉ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์ƒ์„ธํžˆ ์ž‘์„ฑ
78
- 7. ๊ธ€์„ ๋„ˆ๋ฌด ์••์ถ•ํ•˜๊ฑฐ๋‚˜ ํ•จ์ถ•ํ•˜์ง€ ๋ง๊ณ , ์ค‘์š”ํ•œ ๋‚ด์šฉ๊ณผ ์„ธ๋ถ€์‚ฌํ•ญ์„ ๋ชจ๋‘ ํฌํ•จ
79
- 8. ๋ฐ˜๋“œ์‹œ ๋Œ€๋ณธ์˜ ํ๋ฆ„๊ณผ ๋…ผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€
80
- 9. ๋Œ€๋ณธ์˜ ๋ชฉ์ ์ด๋‚˜ ์˜๋„๋ฅผ ํŒŒ์•…ํ•˜๊ณ , ์ด๋ฅผ ์š”์•ฝ์— ๋ฐ˜๋“œ์‹œ ๋ฐ˜์˜
81
- 10. ๋ฐ˜๋“œ์‹œ ์‹œ๊ฐ„ ์ˆœ์„œ๋‚˜ ์‚ฌ๊ฑด์˜ ์ „๊ฐœ ๊ณผ์ •์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฐ˜์˜
82
- 11. ๋“ฑ์žฅ์ธ๋ฌผ, ์žฅ์†Œ, ์‚ฌ๊ฑด ๋“ฑ ์ค‘์š”ํ•œ ์š”์†Œ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ž‘์„ฑ
83
- 12. ๋Œ€๋ณธ์—์„œ ์ „๋‹ฌํ•˜๋Š” ๊ฐ์ •์ด๋‚˜ ๋ถ„์œ„๊ธฐ๋„ ํฌํ•จ
84
- 13. ๋ฐ˜๋“œ์‹œ ๊ธฐ์ˆ ์  ์šฉ์–ด๋‚˜ ์ „๋ฌธ ์šฉ์–ด๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์ด๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์‚ฌ์šฉ
85
- 14. ๋ฐ˜๋“œ์‹œ ํ•ต์‹ฌ ์„น์…˜(์†Œ์ฃผ์ œ)๋ฅผ ํŒŒ์•…ํ•˜์—ฌ ์„น์…˜์— ๋งž๊ฒŒ ๊ธ€์„ ์š”์•ฝํ•˜๋ผ(๊ธ€์˜ ์–‘์„ ๊ณ ๋ คํ•˜์—ฌ ์„น์…˜์˜ ๊ฐœ์ˆ˜๋ฅผ ํƒ„๋ ฅ์ ์œผ๋กœ ์„ค์ •)
86
- 15. ๊ฐ ์„น์…˜์˜ ์ œ๋ชฉ(์†Œ์ฃผ์ œ)์—๋Š” ๋‚ด์šฉ๊ณผ ์–ด์šธ๋ฆฌ๋Š” ์ ์ ˆํ•œ ์ด๋ชจ์ง€๋กœ ์†Œ์ฃผ์ œ๋ฅผ ์‹œ์ž‘ํ•˜๋ผ
87
- 16. ๊ฐ ์„น์…˜์˜ ๋‚ด์šฉ์€ Bullet Point๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€๋…์„ฑ์„ ๋†’์—ฌ๋ผ(๋ฌธ์žฅ ๋‹จ์œ„๋กœ ๊ตฌ๋ถ„)
88
- [์˜ˆ์‹œ]
89
- (๋ณ€๊ฒฝ์ „)
90
- - ์œ ํŠœ๋ธŒ๋ฅผ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์€ ๊ตฌ๋…์ž ์ˆ˜์™€ ์กฐํšŒ์ˆ˜์— ํฐ ๊ด€์‹ฌ์„ ๋‘๊ณ  ๋งค์ผ ์œ ํŠœ๋ธŒ ์ŠคํŠœ๋””์˜ค๋ฅผ ํ™•์ธํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ตฌ๋…์ž๊ฐ€ 100๋ช…, 1,000๋ช…์— ๋„๋‹ฌํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ง€์†์ ์ธ ์„ฑ์žฅ์— ๋„์›€์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ตฌ๋…์ž ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚œ ํ›„์—๋„ ์œ ํŠœ๋ธŒ ์ฑ„๋„ ์šด์˜์— ๋Œ€ํ•œ ๊ฐ์„ ์žก์ง€ ๋ชปํ•ด ํฌ๊ธฐํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
91
- (๋ณ€๊ฒฝํ›„)
92
- - ์œ ํŠœ๋ธŒ๋ฅผ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์€ ๊ตฌ๋…์ž ์ˆ˜์™€ ์กฐํšŒ์ˆ˜์— ํฐ ๊ด€์‹ฌ์„ ๋‘๊ณ  ๋งค์ผ ์œ ํŠœ๋ธŒ ์ŠคํŠœ๋””์˜ค๋ฅผ ํ™•์ธํ•˜๊ฒŒ ๋œ๋‹ค.
93
- - ๊ทธ๋Ÿฌ๋‚˜ ๊ตฌ๋…์ž๊ฐ€ 100๋ช…, 1,000๋ช…์— ๋„๋‹ฌํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ง€์†์ ์ธ ์„ฑ์žฅ์— ๋„์›€์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.
94
- - ๊ตฌ๋…์ž ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚œ ๏ฟฝ๏ฟฝ์—๋„ ์œ ํŠœ๋ธŒ ์ฑ„๋„ ์šด์˜์— ๋Œ€ํ•œ ๊ฐ์„ ์žก์ง€ ๋ชปํ•ด ํฌ๊ธฐํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
95
- 17. ๊ฐ ์„น์…˜์˜ ๋‚ด์šฉ์„ ๋ฐ˜๋“œ์‹œ ์ถฉ์‹คํ•˜๊ฒŒ ์ž‘์„ฑ
96
 
97
- ์ œ๋ชฉ: {title}
98
- ์„ค๋ช…: {description}
99
-
100
- ๋Œ€๋ณธ:
101
- {text}
102
- """
103
- return call_api(prompt, max_tokens=8000, temperature=0.35, top_p=0.95)
104
-
105
- def split_sentences(text):
106
- sentences = re.split(r"(๋‹ˆ๋‹ค|์—์š”|๊ตฌ๋‚˜|ํ•ด์š”|๊ตฐ์š”|๊ฒ ์–ด์š”|์‹œ์˜ค|ํ•ด๋ผ|์˜ˆ์š”|์•„์š”|๋ฐ์š”|๋Œ€์š”|์„ธ์š”|์–ด์š”|๊ฒŒ์š”|๊ตฌ์š”|๊ณ ์š”|๋‚˜์š”|ํ•˜์ฃ )(?![\w])", text)
107
- combined_sentences = []
108
- current_sentence = ""
109
- for i in range(0, len(sentences), 2):
110
- if i + 1 < len(sentences):
111
- sentence = sentences[i] + sentences[i + 1]
112
- else:
113
- sentence = sentences[i]
114
- if len(current_sentence) + len(sentence) > 100: # 100์ž๋ฅผ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ
115
- combined_sentences.append(current_sentence.strip())
116
- current_sentence = sentence.strip()
117
- else:
118
- current_sentence += sentence
119
- if sentence.endswith(('.', '?', '!')):
120
- combined_sentences.append(current_sentence.strip())
121
- current_sentence = ""
122
- if current_sentence:
123
- combined_sentences.append(current_sentence.strip())
124
- return combined_sentences
125
-
126
- def display_script(title, script):
127
- script_sentences = split_sentences(script)
128
- formatted_script = "\n\n".join(script_sentences)
129
- return f"""<div class="script-box">
130
- <details>
131
- <summary>ํด๋ฆญํ•˜์—ฌ ํŽผ์น˜๊ธฐ</summary>
132
- <div class="output-title">{title}</div>
133
- <p style="white-space: pre-wrap;">{formatted_script}</p>
134
- </details>
135
- </div>"""
136
-
137
- def display_summary(title, summary):
138
- return f"""<div class="script-box">
139
- <div class="output-title">{title}</div>
140
- {summary}
141
- </div>"""
142
-
143
- def get_thumbnail_url(thumbnails):
144
- for thumbnail in thumbnails:
145
- if thumbnail.get("width") == 640 and thumbnail.get("height") == 480:
146
- return thumbnail.get("url")
147
- return "640x480 ํฌ๊ธฐ์˜ ์ธ๋„ค์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
148
-
149
- def analyze(url):
150
- # ์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ
151
- yield "์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ ์ค‘...", "์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ ์ค‘...", ""
152
- title, description, script, thumbnails = get_youtube_script(url)
153
- script_content = display_script(title, script)
154
- thumbnail_url = get_thumbnail_url(thumbnails)
155
-
156
- # ์›๋ฌธ ์Šคํฌ๋ฆฝํŠธ ํ‘œ์‹œ ๋ฐ ์š”์•ฝ ์‹œ์ž‘
157
- yield script_content, "์š”์•ฝ ์ƒ์„ฑ ์ค‘...", thumbnail_url
158
 
159
- # ์š”์•ฝ ์ƒ์„ฑ
160
- summary = summarize_text(title, description, script)
161
 
162
- # HTML๋กœ ๋ณ€ํ™˜ (convert_to_html ๋กœ์ง์„ ์ง์ ‘ ํ†ตํ•ฉ)
163
- lines = summary.split('\n')
164
- formatted_lines = []
165
- for line in lines:
166
- line = line.strip()
167
- if line.startswith('####'):
168
- formatted_lines.append(f"<h4>{html.escape(line[4:].strip())}</h4>")
169
- elif line.startswith('###'):
170
- formatted_lines.append(f"<h3>{html.escape(line[3:].strip())}</h3>")
171
- elif line.startswith('##'):
172
- formatted_lines.append(f"<h2>{html.escape(line[2:].strip())}</h2>")
173
- elif line.startswith('#'):
174
- formatted_lines.append(f"<h1>{html.escape(line[1:].strip())}</h1>")
175
- elif line.startswith('- '): # ๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ
176
- content = html.escape(line[2:])
177
- bold_content = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', content)
178
- formatted_lines.append(f"<li>{bold_content}</li>")
179
- elif line: # ์ผ๋ฐ˜ ํ…์ŠคํŠธ (๋นˆ ์ค„ ์ œ์™ธ)
180
- content = html.escape(line)
181
- bold_content = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', content)
182
- formatted_lines.append(f"<p>{bold_content}</p>")
183
- else: # ๋นˆ ์ค„
184
- formatted_lines.append("<br>")
185
-
186
- formatted_summary = '\n'.join(formatted_lines)
187
-
188
- summary_content = f"""<div class="script-box">
189
- <div class="output-title">{html.escape(title)}</div>
190
- {formatted_summary}
191
- </div>"""
192
 
193
- # ์ตœ์ข… ๊ฒฐ๊ณผ ํ‘œ์‹œ
194
- yield script_content, summary_content, thumbnail_url
195
-
196
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค
197
- with gr.Blocks() as demo:
198
- gr.Markdown("## YouTube ์Šคํฌ๋ฆฝํŠธ ์ถ”์ถœ ๋ฐ ์š”์•ฝ ๋„๊ตฌ")
199
- youtube_url_input = gr.Textbox(label="YouTube URL ์ž…๋ ฅ")
200
- analyze_button = gr.Button("๋ถ„์„ํ•˜๊ธฐ")
201
- script_output = gr.HTML(label="์›๋ฌธ ์Šคํฌ๋ฆฝํŠธ")
202
- summary_output = gr.HTML(label="์š”์•ฝ")
203
- thumbnail_output = gr.Textbox(label="์ธ๋„ค์ผ URL (640x480)") # ์ด ์ค„ ์ถ”๊ฐ€
204
-
205
- analyze_button.click(
206
- analyze,
207
- inputs=[youtube_url_input],
208
- outputs=[script_output, summary_output, thumbnail_output] # thumbnail_output ์ถ”๊ฐ€
209
- )
210
-
211
- if __name__ == "__main__":
212
- demo.launch()
 
 
 
 
 
 
1
  import os
2
+ import requests
3
+ import json
4
+ import gradio as gr
5
  import re
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ # Hugging Face ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ๋ถ€ํ„ฐ RapidAPI ํ‚ค์™€ ํ˜ธ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
8
+ AA_KEY = os.getenv("AA_KEY")
9
+ AA_HOST = "youtube-transcriptor.p.rapidapi.com"
10
+
11
+ # ์œ ํŠœ๋ธŒ URL์—์„œ ๋น„๋””์˜ค ID๋ฅผ ์ถ”์ถœํ•˜๋Š” ํ•จ์ˆ˜
12
+ def get_video_id(youtube_url):
13
+ # ์œ ํŠœ๋ธŒ URL ๋˜๋Š” youtu.be ๋‹จ์ถ• URL์—์„œ video_id ์ถ”์ถœ
14
+ video_id_match = re.search(r"(?<=v=)[^#&?]*", youtube_url) or re.search(r"(?<=youtu.be/)[^#&?]*", youtube_url)
15
+ return video_id_match.group(0) if video_id_match else None
16
+
17
+ # ์ž๋ง‰ ์–ธ์–ด ์šฐ์„ ์ˆœ์œ„ ๋ฆฌ์ŠคํŠธ
18
+ LANGUAGE_PRIORITY = ['ko', 'en', 'ja', 'zh']
19
+
20
+ # ์œ ํŠœ๋ธŒ ์ž๋ง‰์„ ์š”์ฒญํ•˜๋Š” ํ•จ์ˆ˜ (์–ธ์–ด ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ ์šฉํ•˜์—ฌ ์‹œ๋„)
21
+ def get_youtube_transcript(youtube_url):
22
+ # ๋น„๋””์˜ค ID ์ถ”์ถœ
23
+ video_id = get_video_id(youtube_url)
24
+ if video_id is None:
25
+ return {"error": "์ž˜๋ชป๋œ ์œ ํŠœ๋ธŒ URL์ž…๋‹ˆ๋‹ค. ๋น„๋””์˜ค ID๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ url = "https://youtube-transcriptor.p.rapidapi.com/transcript"
 
28
 
29
+ headers = {
30
+ "x-rapidapi-key": AA_KEY,
31
+ "x-rapidapi-host": AA_HOST
32
+ }
33
+
34
+ # ์–ธ์–ด ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ์ˆœ๏ฟฝ๏ฟฝ์ ์œผ๋กœ ์š”์ฒญ์„ ์‹œ๋„
35
+ for lang in LANGUAGE_PRIORITY:
36
+ querystring = {"video_id": video_id, "lang": lang}
37
+ response = requests.get(url, headers=headers, params=querystring)
38
+
39
+ # ์ƒํƒœ ์ฝ”๋“œ ํ™•์ธ ๋ฐ ์ „์ฒด ์‘๋‹ต ๋ฐ˜ํ™˜
40
+ if response.status_code == 200:
41
+ try:
42
+ data = response.json()
43
+
44
+ # ์ „์ฒด ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
45
+ return {"language": lang, "data": data}
46
+
47
+ except json.JSONDecodeError as e:
48
+ return {"error": f"JSON ๋””์ฝ”๋”ฉ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"}
49
+
50
+ # ๋ชจ๋“  ์–ธ์–ด์—์„œ ์ž๋ง‰์„ ์ฐพ์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ
51
+ return {"error": "์šฐ์„ ์ˆœ์œ„ ์–ธ์–ด๋กœ ์ž๋ง‰์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."}
52
+
53
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
54
+ def youtube_transcript_interface(youtube_url):
55
+ # ์ž๋ง‰ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
56
+ transcript_data = get_youtube_transcript(youtube_url)
 
 
57
 
58
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
59
+ return json.dumps(transcript_data, ensure_ascii=False, indent=2)
60
+
61
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
62
+ interface = gr.Interface(
63
+ fn=youtube_transcript_interface,
64
+ inputs="text",
65
+ outputs="text",
66
+ title="YouTube ์ž๋ง‰ ์ถ”์ถœ๊ธฐ",
67
+ description="์œ ํŠœ๋ธŒ URL์„ ์ž…๋ ฅํ•˜์„ธ์š”."
68
+ )
69
+
70
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์‹คํ–‰
71
+ interface.launch()