unnastyle commited on
Commit
e2c315e
ยท
verified ยท
1 Parent(s): 2804f9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -47
app.py CHANGED
@@ -1,73 +1,288 @@
 
 
 
1
  import os
2
- import openai
3
 
4
- def refine_with_llm_new(old_changes_summary_text: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  """
6
- 1. (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅ๋ฐ›์•„,
7
- 2. ์ค„๋ฐ”๊ฟˆ/๋„์–ด์“ฐ๊ธฐ๋ฅผ ๋ฌธ๋งฅ์— ๋งž๊ฒŒ ์žฌ๊ตฌ์„ฑ,
8
- 3. ๋‚ด์šฉ์€ ์ ˆ๋Œ€ ์š”์•ฝ/์‚ญ์ œ/์ž„์˜ ๋ณ€๊ฒฝ ๊ธˆ์ง€,
9
- 4. ์˜คํƒ€ยท๋งž์ถค๋ฒ•๋งŒ ์ˆ˜์ •, ๊ทธ ์ˆ˜์ • ๋‚ด์—ญ์„ ๋ณ„๋„๋กœ ์ •๋ฆฌํ•ด ์•Œ๋ ค์ค€๋‹ค.
10
-
11
- ์ถœ๋ ฅ:
12
- (๊ต์ •๏ฟฝ๏ฟฝ ๋ณธ๋ฌธ, ์ˆ˜์ •๋‚ด์—ญ ์ •๋ฆฌ)
13
- - ๊ต์ •๋œ ๋ณธ๋ฌธ(๋ฌธ๋งฅ์ƒ ๋ฌธ๋‹จ ๋‚˜๋ˆ„๊ฑฐ๋‚˜ ๋ถ™์ด๊ธฐ, ๋งž์ถค๋ฒ• ์ˆ˜์ • ๋ฐ˜์˜)
14
- - ์ˆ˜์ •๋‚ด์—ญ(์ด์ „ -> ์ดํ›„, ๋ชจ๋“  ์ˆ˜์ •์‚ฌํ•ญ)
 
 
 
 
 
15
  """
16
  openai.api_key = os.getenv("OPENAI_API_KEY")
17
  if not openai.api_key:
18
- return (
19
- "์˜ค๋ฅ˜: OPENAI_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.",
20
- "์ˆ˜์ •๋‚ด์—ญ ์—†์Œ"
21
- )
22
 
23
  system_prompt = (
24
  "๋‹น์‹ ์€ ํ•œ๊ตญ์–ด ๋ฌธ์žฅ ๊ต์ • ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.\n"
25
- "์•„๋ž˜์˜ [๊ทœ์น™]์„ ์ง€ํ‚ค๋ฉด์„œ ํ…์ŠคํŠธ๋ฅผ ๊ต์ •ํ•ด ์ฃผ์„ธ์š”.\n"
26
  "[๊ทœ์น™]\n"
27
- "1) (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ ํ…์ŠคํŠธ๋ฅผ ์šฐ์„  ๊ทธ๋Œ€๋กœ ๊ธฐ์–ต.\n"
28
- "2) ์ค„๋ฐ”๊ฟˆ, ๋„์–ด์“ฐ๊ธฐ๋ฅผ ๋ฌธ๋งฅ์— ๋งž๊ฒŒ ์žฌ๊ตฌ์„ฑ.\n"
29
- " - ๋ถ„๋ฆฌ๋˜์–ด์•ผ ํ•  ๋ฌธ๋‹จ์€ ๋ถ„๋ฆฌํ•˜๊ณ \n"
30
- " - ๋ถ™์—ฌ์•ผ ํ•  ๋ฌธ๋‹จ์€ ๋ถ™์ด๋˜\n"
31
- " - ๋‚ด์šฉ์€ ์ ˆ๋Œ€ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์š”์•ฝํ•˜์ง€ ๋ง ๊ฒƒ.\n"
32
- "3) ์˜คํƒ€๋‚˜ ๋งž์ถค๋ฒ•์ด ์•ˆ ๋งž๋Š” ๊ฒƒ๋งŒ ์ˆ˜์ •.\n"
33
- " - ์ˆ˜์ •ํ•œ ๋ถ€๋ถ„์€ ๋ชจ๋‘ ๋ณ„๋„๋กœ ์ •๋ฆฌ.\n"
34
- "4) ์ ˆ๋Œ€ ์ž„์˜๋กœ ๋‚ด์šฉ์„ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.\n"
35
- )
36
-
37
- user_prompt = (
38
- f"์•„๋ž˜ ํ…์ŠคํŠธ๋ฅผ [๊ทœ์น™]์— ๋”ฐ๋ผ ๊ต์ •ํ•ด ์ฃผ์„ธ์š”.\n"
39
- f"```\n{old_changes_summary_text}\n```"
40
- "\n\n"
41
  "์ถœ๋ ฅ ํ˜•์‹:\n"
42
- "๊ต์ •๋œ ๋ณธ๋ฌธ(๋ฌธ๋งฅ์ƒ ๋ฌธ๋‹จ ์žฌ๊ตฌ์„ฑ ๋ฐ˜์˜)\n"
 
43
  "=== ์ˆ˜์ •๋‚ด์—ญ ===\n"
44
- "๋ชจ๋“  ๊ต์ • ์‚ฌํ•ญ(์ด์ „ -> ์ดํ›„) ๋ฆฌ์ŠคํŠธ\n"
45
  )
46
 
 
 
47
  try:
48
- # (์˜ˆ: ์ตœ์‹  openai 1.0.0+ ์—์„œ๋Š” ChatCompletion ๋ฐฉ์‹์„ ๊ณ„์† ์ง€์›)
49
  response = openai.ChatCompletion.create(
50
- model="gpt-4o-mini", # ์‹ค์ œ ๋ชจ๋ธ ์กด์žฌ ์—ฌ๋ถ€๋Š” ๋ณ„๋„ ํ™•์ธ ํ•„์š”
51
  messages=[
52
  {"role": "system", "content": system_prompt},
53
- {"role": "user", "content": user_prompt}
54
  ],
55
  temperature=0.0
56
  )
57
  raw_output = response.choices[0].message["content"].strip()
58
  except Exception as e:
59
- return (
60
- f"LLM ํ˜ธ์ถœ ์˜ค๋ฅ˜: {str(e)}",
61
- "์ˆ˜์ •๋‚ด์—ญ ์ •๋ณด ์ƒ์„ฑ ์‹คํŒจ"
62
- )
63
 
64
- # ์•„๋ž˜๋Š” ๊ฐ€์ •๋œ ์ถœ๋ ฅ ํ˜•์‹:
65
- # ๊ต์ •๋œ ๋ณธ๋ฌธ
 
66
  # === ์ˆ˜์ •๋‚ด์—ญ ===
67
- # (์ˆ˜์ • ์‚ฌํ•ญ๋“ค)
68
- if "=== ์ˆ˜์ •๋‚ด์—ญ ===" in raw_output:
69
- corrected_text, changes_text = raw_output.split("=== ์ˆ˜์ •๋‚ด์—ญ ===", 1)
70
- return corrected_text.strip(), changes_text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  else:
72
- # ์ˆ˜์ •๋‚ด์—ญ ๊ตฌ๋ถ„์ด ์—†์œผ๋ฉด, ํ†ต์งธ๋กœ ๋ณธ๋ฌธ์œผ๋กœ ๋ฐ˜ํ™˜
73
- return raw_output, "์ˆ˜์ •๋‚ด์—ญ ๊ตฌ๋ถ„ ์—†์Œ"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import re
4
  import os
5
+ import openai # openai ์ตœ์‹ ๋ฒ„์ „ (>=1.0.0) ๊ฐ€์ •
6
 
7
+ def debug_print(*args):
8
+ """๋””๋ฒ„๊น…์šฉ ์ฝ˜์†” ์ถœ๋ ฅ"""
9
+ print("[DEBUG]", *args)
10
+
11
+ ########################################################
12
+ # (1) ๊ธฐ์กด ํ•จ์ˆ˜๋“ค: parse_track_number, fetch_script, show_next_field
13
+ ########################################################
14
+
15
+ def parse_track_number(input_text):
16
+ debug_print("parse_track_number() called with:", input_text)
17
+ match = re.search(r'<track[^>]*\skind="captions"[^>]*\ssrc="([^"]+)"', input_text)
18
+ if not match:
19
+ debug_print("ํŠธ๋ž™ ํƒœ๊ทธ(kinds='captions')์—์„œ src ์ถ”์ถœ ์‹คํŒจ")
20
+ return None
21
+
22
+ track_number = match.group(1).strip()
23
+ debug_print("์ดˆ๊ธฐ ์ถ”์ถœ track_number:", track_number)
24
+
25
+ # blob: ์ œ๊ฑฐ
26
+ if 'blob:' in track_number:
27
+ track_number = track_number.replace('blob:', '')
28
+ debug_print("blob: ์ œ๊ฑฐ ํ›„:", track_number)
29
+
30
+ # https://player.vimeo.com ์ œ๊ฑฐ
31
+ if 'https://player.vimeo.com' in track_number:
32
+ track_number = track_number.replace('https://player.vimeo.com', '')
33
+ debug_print("๋„๋ฉ”์ธ ์ œ๊ฑฐ ํ›„:", track_number)
34
+
35
+ track_number = track_number.strip()
36
+ if not track_number.startswith('/'):
37
+ track_number = '/' + track_number
38
+ debug_print("์•ž์— '/' ์ถ”๊ฐ€ ํ›„:", track_number)
39
+
40
+ debug_print("์ตœ์ข… track_number:", track_number)
41
+ return track_number
42
+
43
+ def fetch_script(url):
44
+ """URL(๋ฌธ์ž์—ด)์—์„œ ํŠธ๋ž™๋ฒˆํ˜ธ ์ถ”์ถœ ํ›„, Vimeo ์ž๋ง‰ URL๋กœ GET ์š”์ฒญ."""
45
+ track_number = parse_track_number(url)
46
+ if not track_number:
47
+ return "ํŠธ๋ž™๋ฒˆํ˜ธ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ: <track kind='captions' src='/1234567.vtt' ...>"
48
+
49
+ target_url = f"https://player.vimeo.com{track_number}"
50
+ debug_print("์ ‘์†ํ•  URL:", target_url)
51
+ try:
52
+ resp = requests.get(target_url)
53
+ if resp.status_code == 200:
54
+ debug_print("์ž๋ง‰ ์Šคํฌ๋ฆฝํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ ์„ฑ๊ณต")
55
+ return resp.text
56
+ else:
57
+ debug_print("HTTP ์—๋Ÿฌ:", resp.status_code)
58
+ return f"HTTP ์—๋Ÿฌ: {resp.status_code}"
59
+ except Exception as e:
60
+ debug_print("์˜ˆ์™ธ ๋ฐœ์ƒ:", str(e))
61
+ return f"์—๋Ÿฌ ๋ฐœ์ƒ: {str(e)}"
62
+
63
+ def show_next_field(num_visible):
64
+ """
65
+ + ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, URL ์ž…๋ ฅ์นธ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•ด์„œ ์ตœ๋Œ€ 5๊ฐœ๊นŒ์ง€ ํ‘œ์‹œ.
66
+ """
67
+ debug_print("show_next_field() called. Current:", num_visible)
68
+ if num_visible < 5:
69
+ num_visible += 1
70
+ updates = []
71
+ for i in range(1, 6):
72
+ updates.append(gr.update(visible=(i <= num_visible)))
73
+
74
+ return [num_visible] + updates
75
+
76
+ ########################################################
77
+ # (2) (๊ธฐ์กด) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ ๋งŒ๋“ค๊ธฐ
78
+ ########################################################
79
+
80
+ def is_vtt_timeline_line(line: str) -> bool:
81
+ # ์˜ˆ: 00:49:33.920 --> 00:49:38.700
82
+ pattern = r'^\d{2}:\d{2}:\d{2}(\.\d+)?\s*-->\s*\d{2}:\d{2}:\d{2}(\.\d+)?$'
83
+ return bool(re.match(pattern, line.strip()))
84
+
85
+ def is_number_line(line: str) -> bool:
86
+ # ์˜ˆ: 1, 2., 3)
87
+ pattern = r'^\d+[\.\)]?$'
88
+ return bool(re.match(pattern, line.strip()))
89
+
90
+ def is_webvtt_header(line: str) -> bool:
91
+ # ์˜ˆ: WEBVTT
92
+ return line.strip() == "WEBVTT"
93
+
94
+ def combine_scripts(script1, script2, script3, script4, script5):
95
+ """
96
+ - (๊ธฐ์กด) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ: ์›๋ณธ ๊ทธ๋Œ€๋กœ ํ•ฉ์นจ + [๊ฐ•์˜N] ํ—ค๋”
97
+ - (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ: ํƒ€์ž„๋ผ์ธ/์ˆซ์ž/WEBVTT ์ œ๊ฑฐ + [๊ฐ•์˜N] ํ—ค๋”
98
+ """
99
+ scripts = [script1, script2, script3, script4, script5]
100
+ merged_full = []
101
+ merged_filtered = []
102
+
103
+ for idx, sc in enumerate(scripts, start=1):
104
+ if not sc or not sc.strip():
105
+ continue
106
+
107
+ lines = sc.splitlines()
108
+
109
+ # (A) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ
110
+ merged_full.append(f"[๊ฐ•์˜{idx}]")
111
+ merged_full.extend(lines)
112
+
113
+ # (B) ์ˆ˜์ •์‚ฌํ•ญ
114
+ merged_filtered.append(f"[๊ฐ•์˜{idx}]")
115
+ for line in lines:
116
+ if is_vtt_timeline_line(line):
117
+ continue
118
+ if is_number_line(line):
119
+ continue
120
+ if is_webvtt_header(line):
121
+ continue
122
+ merged_filtered.append(line)
123
+
124
+ old_combined_script = "\n".join(merged_full)
125
+ old_changes_summary = "\n".join(merged_filtered)
126
+ return old_combined_script, old_changes_summary
127
+
128
+ ########################################################
129
+ # (3) LLM ํ˜ธ์ถœ ๋กœ์ง - ์ƒˆ๋กœ์šด ์ง€์‹œ์‚ฌํ•ญ
130
+ ########################################################
131
+
132
+ def refine_with_llm(old_changes_summary_text):
133
  """
134
+ [์ƒˆ๋กœ์šด ์š”๊ตฌ์‚ฌํ•ญ]:
135
+ 1. (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ ๋‚ด์šฉ ์šฐ์„  ๊ธฐ์–ต
136
+ 2. ์ค„๋ฐ”๊ฟˆ/๋„์–ด์“ฐ๊ธฐ ๋ฌธ๋งฅ์— ๋งž๊ฒŒ ์žฌ๊ตฌ์„ฑ (๋ถ™์ด๊ณ /๋‚˜๋ˆ„๊ณ )
137
+ 3. ์ ˆ๋Œ€ ๋‚ด์šฉ ์ค„์ด๊ฑฐ๋‚˜ ์š”์•ฝ/์‚ญ์ œ/์ž„์˜๋ณ€๊ฒฝํ•˜์ง€ ๋ง๊ธฐ
138
+ 4. ์˜คํƒ€/๋งž์ถค๋ฒ•๋งŒ ์ˆ˜์ •
139
+ 5. ์ˆ˜์ •ํ•œ ๋ถ€๋ถ„ ๋ชจ๋‘ ์ •๋ฆฌํ•˜์—ฌ ์•Œ๋ ค์ฃผ๊ธฐ
140
+
141
+ ์ถœ๋ ฅํ˜•์‹:
142
+ (๊ต์ •๋œ ๋ณธ๋ฌธ, ์ˆ˜์ •๋‚ด์—ญ)
143
+
144
+ LLM ์‘๋‹ต ์˜ˆ์‹œ:
145
+ ๊ต์ •๋œ ๋ณธ๋ฌธ
146
+ === ์ˆ˜์ •๋‚ด์—ญ ===
147
+ (์ด์ „ -> ์ดํ›„ ๋ชฉ๋ก)
148
  """
149
  openai.api_key = os.getenv("OPENAI_API_KEY")
150
  if not openai.api_key:
151
+ return "์˜ค๋ฅ˜: OPENAI_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.", "์ˆ˜์ •๋‚ด์—ญ ์—†์Œ"
 
 
 
152
 
153
  system_prompt = (
154
  "๋‹น์‹ ์€ ํ•œ๊ตญ์–ด ๋ฌธ์žฅ ๊ต์ • ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.\n"
 
155
  "[๊ทœ์น™]\n"
156
+ "1) (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ ํ…์ŠคํŠธ๋ฅผ ์šฐ์„  ๊ทธ๋Œ€๋กœ.\n"
157
+ "2) ์ค„๋ฐ”๊ฟˆ/๋„์–ด์“ฐ๊ธฐ๋ฅผ ๋ฌธ๋งฅ์— ๋งž๊ฒŒ ์žฌ๊ตฌ์„ฑ.\n"
158
+ " - ๋ถ™์—ฌ์•ผ ํ•  ๋ฌธ๋‹จ์€ ๋ถ™์ด๊ณ \n"
159
+ " - ๋ถ„๋ฆฌํ•ด์•ผ ํ•  ๋ฌธ๋‹จ์€ ๋ถ„๋ฆฌ.\n"
160
+ "3) ์ ˆ๋Œ€ ๋‚ด์šฉ์„ ์š”์•ฝ/์‚ญ์ œ/์ž„์˜ ๋ณ€๊ฒฝํ•˜์ง€ ๋ง ๊ฒƒ.\n"
161
+ "4) ์˜คํƒ€๋‚˜ ๋งž์ถค๋ฒ•๋งŒ ์ˆ˜์ •.\n"
162
+ "5) ์ˆ˜์ •ํ•œ ๋ถ€๋ถ„์„ ๋ชจ๋‘ ์ •๋ฆฌํ•ด ์•Œ๋ ค์ค„ ๊ฒƒ.\n"
 
 
 
 
 
 
 
163
  "์ถœ๋ ฅ ํ˜•์‹:\n"
164
+ "=== ๊ต์ •๋œ ๋ณธ๋ฌธ ===\n"
165
+ "(์žฌ๊ตฌ์„ฑ+๋งž์ถค๋ฒ• ์ˆ˜์ •๋œ ๋ณธ๋ฌธ)\n"
166
  "=== ์ˆ˜์ •๋‚ด์—ญ ===\n"
167
+ "(์ด์ „ -> ์ดํ›„) ๋“ฑ ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ ๋ฆฌ์ŠคํŠธ.\n"
168
  )
169
 
170
+ user_prompt = f"์•„๋ž˜ ํ…์ŠคํŠธ๋ฅผ ๊ต์ •ํ•ด ์ฃผ์„ธ์š”:\n```\n{old_changes_summary_text}\n```"
171
+
172
  try:
 
173
  response = openai.ChatCompletion.create(
174
+ model="gpt-4o-mini", # ์‹ค์ œ ์กด์žฌX ๊ฐ€๋Šฅ์„ฑ. ํ•„์š”์‹œ gpt-3.5-turbo ๋“ฑ ์‚ฌ์šฉ
175
  messages=[
176
  {"role": "system", "content": system_prompt},
177
+ {"role": "user", "content": user_prompt},
178
  ],
179
  temperature=0.0
180
  )
181
  raw_output = response.choices[0].message["content"].strip()
182
  except Exception as e:
183
+ return f"LLM ํ˜ธ์ถœ ์˜ค๋ฅ˜: {str(e)}", "์ˆ˜์ •๋‚ด์—ญ ์ƒ์„ฑ ์‹คํŒจ"
 
 
 
184
 
185
+ # ์ถœ๋ ฅ ์˜ˆ์‹œ:
186
+ # === ๊ต์ •๋œ ๋ณธ๋ฌธ ===
187
+ # (๋ณธ๋ฌธ)
188
  # === ์ˆ˜์ •๋‚ด์—ญ ===
189
+ # (์ˆ˜์ •๋ฆฌ์ŠคํŠธ)
190
+ corrected_sep = "=== ๊ต์ •๋œ ๋ณธ๋ฌธ ==="
191
+ changes_sep = "=== ์ˆ˜์ •๋‚ด์—ญ ==="
192
+
193
+ corrected_text = ""
194
+ changes_text = ""
195
+
196
+ if corrected_sep in raw_output and changes_sep in raw_output:
197
+ # ๊ต์ •๋œ ๋ณธ๋ฌธ
198
+ part1 = raw_output.split(corrected_sep, 1)[1]
199
+ if changes_sep in part1:
200
+ corrected_text, changes_text = part1.split(changes_sep, 1)
201
+ corrected_text = corrected_text.strip()
202
+ changes_text = changes_text.strip()
203
+ else:
204
+ corrected_text = part1.strip()
205
+ changes_text = "์ˆ˜์ •๋‚ด์—ญ ๊ตฌ๋ถ„ ์—†์Œ"
206
  else:
207
+ corrected_text = raw_output
208
+ changes_text = "LLM ์‘๋‹ต์— ์ˆ˜์ •๋‚ด์—ญ ๊ตฌ๋ถ„ ์—†์Œ"
209
+
210
+ return corrected_text, changes_text
211
+
212
+ ########################################################
213
+ # (4) build_app: Gradio UI
214
+ ########################################################
215
+
216
+ def build_app():
217
+ with gr.Blocks() as demo:
218
+ gr.Markdown("## ๊ฐ•์˜ URL ์ž…๋ ฅ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์ฒ˜๋ฆฌ")
219
+
220
+ num_visible = gr.State(value=2)
221
+ script_outputs = []
222
+ row_list = []
223
+
224
+ # ์ตœ๋Œ€ 5๊ฐœ URL ์ž…๋ ฅ
225
+ for i in range(1, 6):
226
+ with gr.Row(visible=(i <= 2)) as row:
227
+ with gr.Column():
228
+ url_inp = gr.Textbox(
229
+ label=f"๊ฐ•์˜{i} URL ์ž…๋ ฅ",
230
+ placeholder="์˜ˆ: <track kind='captions' src='/1234567.vtt' ...>"
231
+ )
232
+ fetch_btn = gr.Button("์Šคํฌ๋ฆฝํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ")
233
+ script_box = gr.Textbox(label=f"๊ฐ•์˜{i} ์Šคํฌ๋ฆฝํŠธ", lines=8)
234
+
235
+ fetch_btn.click(fetch_script, inputs=url_inp, outputs=script_box)
236
+ row_list.append(row)
237
+ script_outputs.append(script_box)
238
+
239
+ plus_button = gr.Button("+")
240
+ plus_button.click(show_next_field, inputs=num_visible, outputs=[num_visible]+row_list)
241
+
242
+ # (๊ธฐ์กด) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ ๋งŒ๋“ค๊ธฐ
243
+ with gr.Row():
244
+ old_combine_btn = gr.Button("(๊ธฐ์กด) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ ๋งŒ๋“ค๊ธฐ")
245
+ with gr.Row():
246
+ old_combined_box = gr.Textbox(
247
+ label="(๊ธฐ์กด) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ",
248
+ lines=8
249
+ )
250
+ old_changes_box = gr.Textbox(
251
+ label="(๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ",
252
+ lines=8
253
+ )
254
+
255
+ # combine_scripts -> (๊ธฐ์กด) ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ, (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ
256
+ old_combine_btn.click(
257
+ fn=combine_scripts,
258
+ inputs=script_outputs,
259
+ outputs=[old_combined_box, old_changes_box]
260
+ )
261
+
262
+ # LLM ๊ต์ •
263
+ with gr.Row():
264
+ llm_btn = gr.Button("LLM(์ƒˆ ๊ต์ •) - ๊ฐ•์˜๋‚ด์šฉ ์ •๋ฆฌ")
265
+
266
+ with gr.Row():
267
+ corrected_box = gr.Textbox(
268
+ label="๊ต์ •๋œ ๋ณธ๋ฌธ",
269
+ lines=10,
270
+ placeholder="์ค„๋ฐ”๊ฟˆ/๋„์–ด์“ฐ๊ธฐ ์žฌ๊ตฌ์„ฑ + ๋งž์ถค๋ฒ• ์ˆ˜์ • ๊ฒฐ๊ณผ"
271
+ )
272
+ changes_box = gr.Textbox(
273
+ label="์ˆ˜์ •๋‚ด์—ญ",
274
+ lines=8,
275
+ placeholder="์˜คํƒ€/๋งž์ถค๋ฒ• ์ˆ˜์ •๋œ ๋ถ€๋ถ„ ์š”์•ฝ"
276
+ )
277
+
278
+ llm_btn.click(
279
+ fn=refine_with_llm,
280
+ inputs=old_changes_box, # (๊ธฐ์กด) ์ˆ˜์ •์‚ฌํ•ญ
281
+ outputs=[corrected_box, changes_box]
282
+ )
283
+
284
+ return demo
285
+
286
+ if __name__ == "__main__":
287
+ demo = build_app()
288
+ demo.launch()