jpjp9292 commited on
Commit
b23e267
ยท
verified ยท
1 Parent(s): d299e32

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +164 -164
app.py CHANGED
@@ -142,164 +142,12 @@
142
 
143
 
144
 
145
- import streamlit as st
146
- import yt_dlp
147
- import os
148
- from pathlib import Path
149
- import random
150
- import time
151
-
152
- # ํŽ˜์ด์ง€ ์„ค์ •
153
- st.set_page_config(page_title="Simple YouTube Downloader", page_icon="๐Ÿ“บ")
154
- st.title("Simple YouTube Downloader ๐Ÿ“บ")
155
-
156
- # ์Šคํƒ€์ผ ์„ค์ •
157
- st.markdown("""
158
- <style>
159
- .stButton > button {
160
- width: 100%;
161
- height: 60px;
162
- }
163
- </style>
164
- """, unsafe_allow_html=True)
165
-
166
- # ๋‹ค์šด๋กœ๋“œ ํด๋” ์ƒ์„ฑ
167
- output_dir = Path("downloads")
168
- output_dir.mkdir(exist_ok=True)
169
-
170
- def download_video(url, progress_bar, status_text):
171
- try:
172
- user_agents = [
173
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
174
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15',
175
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0'
176
- ]
177
-
178
- ydl_opts = {
179
- 'format': 'best',
180
- 'outtmpl': str(output_dir / '%(title)s.%(ext)s'),
181
- 'quiet': True,
182
- 'no_warnings': True,
183
- 'cookiefile': 'youtube.com_cookies.txt',
184
- 'user_agent': random.choice(user_agents),
185
- 'referer': 'https://www.youtube.com/',
186
- 'http_chunk_size': random.randint(10000000, 15000000),
187
- 'retries': 5, # ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ 5๋กœ ๋Š˜๋ฆผ
188
- 'sleep_interval': 3,
189
- 'max_sleep_interval': 8,
190
- 'headers': {
191
- 'Accept-Language': 'en-US,en;q=0.9',
192
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
193
- },
194
- }
195
-
196
- def progress_hook(d):
197
- if d['status'] == 'downloading':
198
- try:
199
- progress = d['downloaded_bytes'] / d['total_bytes']
200
- progress_bar.progress(progress)
201
- status_text.text(f"๋‹ค์šด๋กœ๋“œ ์ค‘: {progress:.1%}")
202
- except:
203
- status_text.text("๋‹ค์šด๋กœ๋“œ ์ค‘...")
204
- elif d['status'] == 'finished':
205
- progress_bar.progress(1.0)
206
- status_text.text("์ฒ˜๋ฆฌ ์™„๋ฃŒ!")
207
-
208
- ydl_opts['progress_hooks'] = [progress_hook]
209
-
210
- for attempt in range(5): # ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ 5๋กœ ์„ค์ •
211
- try:
212
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
213
- info = ydl.extract_info(url, download=True)
214
- filename = ydl.prepare_filename(info)
215
- return filename
216
- except yt_dlp.utils.ExtractorError as e:
217
- if "Sign in to confirm you're not a bot" in str(e):
218
- status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
219
- time.sleep(random.uniform(3, 5))
220
- continue
221
- status_text.text(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
222
- st.error(f"๋‹ค์šด๋กœ๋“œ ์‹คํŒจ: {str(e)}") # ์‹คํŒจ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
223
- st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ URL์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.") # ์ถ”๊ฐ€ ๋ฉ”๋ชจ
224
- return None
225
- except Exception as e:
226
- if attempt < 4:
227
- status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
228
- time.sleep(2)
229
- continue
230
- status_text.text(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
231
- st.error(f"๋‹ค์šด๋กœ๋“œ ์‹คํŒจ: {str(e)}") # ์‹คํŒจ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
232
- st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ URL์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.") # ์ถ”๊ฐ€ ๋ฉ”๋ชจ
233
- return None
234
-
235
- except Exception as e:
236
- st.error(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
237
- return None
238
-
239
- # ์ฟ ํ‚ค ํŒŒ์ผ ์ฒดํฌ
240
- if not os.path.exists('youtube.com_cookies.txt'):
241
- st.warning("โš ๏ธ 'youtube.com_cookies.txt' ํŒŒ์ผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. YouTube์— ๋กœ๊ทธ์ธ๋œ ์ฟ ํ‚ค ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
242
- uploaded_file = st.file_uploader("์ฟ ํ‚ค ํŒŒ์ผ ์—…๋กœ๋“œ", type=['txt'])
243
- if uploaded_file is not None:
244
- with open('youtube.com_cookies.txt', 'wb') as f:
245
- f.write(uploaded_file.getvalue())
246
- st.success("โœ… ์ฟ ํ‚ค ํŒŒ์ผ์ด ์—…๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
247
- st.rerun()
248
-
249
- # ๋ฉ”์ธ UI
250
- if 'video_url' not in st.session_state:
251
- st.session_state.video_url = ""
252
-
253
- video_url = st.text_input("YouTube URL ์ž…๋ ฅ:",
254
- value=st.session_state.video_url,
255
- placeholder="https://www.youtube.com/watch?v=...")
256
-
257
- # ๋ฒ„ํŠผ์„ ๊ฐ™์€ ์ค„์— ๋ฐฐ์น˜
258
- col1, col2 = st.columns(2)
259
-
260
- with col1:
261
- download_button = st.button("๋‹ค์šด๋กœ๋“œ", type="primary", key="download_btn", use_container_width=True)
262
-
263
- with col2:
264
- reset_button = st.button("์ดˆ๊ธฐํ™”", key="reset_btn", use_container_width=True)
265
-
266
- # ๋‹ค์šด๋กœ๋“œ ๋กœ์ง
267
- if download_button:
268
- if video_url:
269
- progress_bar = st.progress(0)
270
- status_text = st.empty()
271
-
272
- downloaded_file = download_video(video_url, progress_bar, status_text)
273
-
274
- if downloaded_file and os.path.exists(downloaded_file):
275
- with open(downloaded_file, 'rb') as file:
276
- st.download_button(
277
- label="โฌ‡๏ธ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ",
278
- data=file,
279
- file_name=os.path.basename(downloaded_file),
280
- mime="video/mp4",
281
- use_container_width=True
282
- )
283
- else:
284
- st.warning("โš ๏ธ YouTube URL์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
285
-
286
- # ์ดˆ๊ธฐํ™” ๋กœ์ง
287
- if reset_button:
288
- st.session_state.video_url = ""
289
- st.rerun()
290
-
291
-
292
-
293
  # import streamlit as st
294
  # import yt_dlp
295
  # import os
296
  # from pathlib import Path
297
  # import random
298
  # import time
299
- # import logging
300
-
301
- # # ๋กœ๊น… ์„ค์ •
302
- # logging.basicConfig(level=logging.ERROR, filename='error.log')
303
 
304
  # # ํŽ˜์ด์ง€ ์„ค์ •
305
  # st.set_page_config(page_title="Simple YouTube Downloader", page_icon="๐Ÿ“บ")
@@ -335,10 +183,10 @@ if reset_button:
335
  # 'cookiefile': 'youtube.com_cookies.txt',
336
  # 'user_agent': random.choice(user_agents),
337
  # 'referer': 'https://www.youtube.com/',
338
- # 'http_chunk_size': random.randint(100000000, 150000000),
339
- # 'retries': 5, # ์žฌ์‹œ๋„ ํšŸ์ˆ˜
340
  # 'sleep_interval': 3,
341
- # 'max_sleep_interval': 10,
342
  # 'headers': {
343
  # 'Accept-Language': 'en-US,en;q=0.9',
344
  # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
@@ -351,8 +199,7 @@ if reset_button:
351
  # progress = d['downloaded_bytes'] / d['total_bytes']
352
  # progress_bar.progress(progress)
353
  # status_text.text(f"๋‹ค์šด๋กœ๋“œ ์ค‘: {progress:.1%}")
354
- # except Exception as e:
355
- # logging.error(f"Progress Hook Error: {e}")
356
  # status_text.text("๋‹ค์šด๋กœ๋“œ ์ค‘...")
357
  # elif d['status'] == 'finished':
358
  # progress_bar.progress(1.0)
@@ -371,21 +218,22 @@ if reset_button:
371
  # status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
372
  # time.sleep(random.uniform(3, 5))
373
  # continue
374
- # logging.error(f"Extractor Error: {str(e)}")
375
- # st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ URL์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.")
 
376
  # return None
377
  # except Exception as e:
378
- # logging.error(f"Download Error: {str(e)}")
379
  # if attempt < 4:
380
  # status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
381
  # time.sleep(2)
382
  # continue
383
- # st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ ๋‹ค๋ฅธ URL์„ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
 
 
384
  # return None
385
 
386
  # except Exception as e:
387
- # logging.error(f"General Error: {str(e)}")
388
- # st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ ๋‹ค๋ฅธ URL์„ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
389
  # return None
390
 
391
  # # ์ฟ ํ‚ค ํŒŒ์ผ ์ฒดํฌ
@@ -438,5 +286,157 @@ if reset_button:
438
  # # ์ดˆ๊ธฐํ™” ๋กœ์ง
439
  # if reset_button:
440
  # st.session_state.video_url = ""
441
- # st.rerun() # st.experimental_rerun()์„ st.rerun()์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
 
142
 
143
 
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  # import streamlit as st
146
  # import yt_dlp
147
  # import os
148
  # from pathlib import Path
149
  # import random
150
  # import time
 
 
 
 
151
 
152
  # # ํŽ˜์ด์ง€ ์„ค์ •
153
  # st.set_page_config(page_title="Simple YouTube Downloader", page_icon="๐Ÿ“บ")
 
183
  # 'cookiefile': 'youtube.com_cookies.txt',
184
  # 'user_agent': random.choice(user_agents),
185
  # 'referer': 'https://www.youtube.com/',
186
+ # 'http_chunk_size': random.randint(10000000, 15000000),
187
+ # 'retries': 5, # ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ 5๋กœ ๋Š˜๋ฆผ
188
  # 'sleep_interval': 3,
189
+ # 'max_sleep_interval': 8,
190
  # 'headers': {
191
  # 'Accept-Language': 'en-US,en;q=0.9',
192
  # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 
199
  # progress = d['downloaded_bytes'] / d['total_bytes']
200
  # progress_bar.progress(progress)
201
  # status_text.text(f"๋‹ค์šด๋กœ๋“œ ์ค‘: {progress:.1%}")
202
+ # except:
 
203
  # status_text.text("๋‹ค์šด๋กœ๋“œ ์ค‘...")
204
  # elif d['status'] == 'finished':
205
  # progress_bar.progress(1.0)
 
218
  # status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
219
  # time.sleep(random.uniform(3, 5))
220
  # continue
221
+ # status_text.text(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
222
+ # st.error(f"๋‹ค์šด๋กœ๋“œ ์‹คํŒจ: {str(e)}") # ์‹คํŒจ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
223
+ # st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ URL์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.") # ์ถ”๊ฐ€ ๋ฉ”๋ชจ
224
  # return None
225
  # except Exception as e:
 
226
  # if attempt < 4:
227
  # status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
228
  # time.sleep(2)
229
  # continue
230
+ # status_text.text(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
231
+ # st.error(f"๋‹ค์šด๋กœ๋“œ ์‹คํŒจ: {str(e)}") # ์‹คํŒจ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
232
+ # st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ URL์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.") # ์ถ”๊ฐ€ ๋ฉ”๋ชจ
233
  # return None
234
 
235
  # except Exception as e:
236
+ # st.error(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
 
237
  # return None
238
 
239
  # # ์ฟ ํ‚ค ํŒŒ์ผ ์ฒดํฌ
 
286
  # # ์ดˆ๊ธฐํ™” ๋กœ์ง
287
  # if reset_button:
288
  # st.session_state.video_url = ""
289
+ # st.rerun()
290
+
291
+
292
+
293
+ import streamlit as st
294
+ import yt_dlp
295
+ import os
296
+ from pathlib import Path
297
+ import random
298
+ import time
299
+ import logging
300
+
301
+ # ๋กœ๊น… ์„ค์ •
302
+ logging.basicConfig(level=logging.ERROR, filename='error.log')
303
+
304
+ # ํŽ˜์ด์ง€ ์„ค์ •
305
+ st.set_page_config(page_title="Simple YouTube Downloader", page_icon="๐Ÿ“บ")
306
+ st.title("Simple YouTube Downloader ๐Ÿ“บ")
307
+
308
+ # ์Šคํƒ€์ผ ์„ค์ •
309
+ st.markdown("""
310
+ <style>
311
+ .stButton > button {
312
+ width: 100%;
313
+ height: 60px;
314
+ }
315
+ </style>
316
+ """, unsafe_allow_html=True)
317
+
318
+ # ๋‹ค์šด๋กœ๋“œ ํด๋” ์ƒ์„ฑ
319
+ output_dir = Path("downloads")
320
+ output_dir.mkdir(exist_ok=True)
321
+
322
+ def download_video(url, progress_bar, status_text):
323
+ try:
324
+ user_agents = [
325
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
326
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15',
327
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0'
328
+ ]
329
+
330
+ ydl_opts = {
331
+ 'format': 'best',
332
+ 'outtmpl': str(output_dir / '%(title)s.%(ext)s'),
333
+ 'quiet': True,
334
+ 'no_warnings': True,
335
+ 'cookiefile': 'youtube.com_cookies.txt',
336
+ 'user_agent': random.choice(user_agents),
337
+ 'referer': 'https://www.youtube.com/',
338
+ 'http_chunk_size': random.randint(100000000, 150000000),
339
+ 'retries': 5, # ์žฌ์‹œ๋„ ํšŸ์ˆ˜
340
+ 'sleep_interval': 3,
341
+ 'max_sleep_interval': 10,
342
+ 'headers': {
343
+ 'Accept-Language': 'en-US,en;q=0.9',
344
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
345
+ },
346
+ }
347
+
348
+ def progress_hook(d):
349
+ if d['status'] == 'downloading':
350
+ try:
351
+ progress = d['downloaded_bytes'] / d['total_bytes']
352
+ progress_bar.progress(progress)
353
+ status_text.text(f"๋‹ค์šด๋กœ๋“œ ์ค‘: {progress:.1%}")
354
+ except Exception as e:
355
+ logging.error(f"Progress Hook Error: {e}")
356
+ status_text.text("๋‹ค์šด๋กœ๋“œ ์ค‘...")
357
+ elif d['status'] == 'finished':
358
+ progress_bar.progress(1.0)
359
+ status_text.text("์ฒ˜๋ฆฌ ์™„๋ฃŒ!")
360
+
361
+ ydl_opts['progress_hooks'] = [progress_hook]
362
+
363
+ for attempt in range(5): # ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ 5๋กœ ์„ค์ •
364
+ try:
365
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
366
+ info = ydl.extract_info(url, download=True)
367
+ filename = ydl.prepare_filename(info)
368
+ return filename
369
+ except yt_dlp.utils.ExtractorError as e:
370
+ if "Sign in to confirm you're not a bot" in str(e):
371
+ status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
372
+ time.sleep(random.uniform(3, 5))
373
+ continue
374
+ logging.error(f"Extractor Error: {str(e)}")
375
+ st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ URL์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.")
376
+ return None
377
+ except Exception as e:
378
+ logging.error(f"Download Error: {str(e)}")
379
+ if attempt < 4:
380
+ status_text.text(f"์žฌ์‹œ๋„ ์ค‘... ({attempt + 1}/5)")
381
+ time.sleep(2)
382
+ continue
383
+ st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ ๋‹ค๋ฅธ URL์„ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
384
+ return None
385
+
386
+ except Exception as e:
387
+ logging.error(f"General Error: {str(e)}")
388
+ st.info("๋ฌธ์ œ๊ฐ€ ์ง€์†๋  ๊ฒฝ์šฐ ๋‹ค๋ฅธ URL์„ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
389
+ return None
390
+
391
+ # ์ฟ ํ‚ค ํŒŒ์ผ ์ฒดํฌ
392
+ if not os.path.exists('youtube.com_cookies.txt'):
393
+ st.warning("โš ๏ธ 'youtube.com_cookies.txt' ํŒŒ์ผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. YouTube์— ๋กœ๊ทธ์ธ๋œ ์ฟ ํ‚ค ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
394
+ uploaded_file = st.file_uploader("์ฟ ํ‚ค ํŒŒ์ผ ์—…๋กœ๋“œ", type=['txt'])
395
+ if uploaded_file is not None:
396
+ with open('youtube.com_cookies.txt', 'wb') as f:
397
+ f.write(uploaded_file.getvalue())
398
+ st.success("โœ… ์ฟ ํ‚ค ํŒŒ์ผ์ด ์—…๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
399
+ st.rerun()
400
+
401
+ # ๋ฉ”์ธ UI
402
+ if 'video_url' not in st.session_state:
403
+ st.session_state.video_url = ""
404
+
405
+ video_url = st.text_input("YouTube URL ์ž…๋ ฅ:",
406
+ value=st.session_state.video_url,
407
+ placeholder="https://www.youtube.com/watch?v=...")
408
+
409
+ # ๋ฒ„ํŠผ์„ ๊ฐ™์€ ์ค„์— ๋ฐฐ์น˜
410
+ col1, col2 = st.columns(2)
411
+
412
+ with col1:
413
+ download_button = st.button("๋‹ค์šด๋กœ๋“œ", type="primary", key="download_btn", use_container_width=True)
414
+
415
+ with col2:
416
+ reset_button = st.button("์ดˆ๊ธฐํ™”", key="reset_btn", use_container_width=True)
417
+
418
+ # ๋‹ค์šด๋กœ๋“œ ๋กœ์ง
419
+ if download_button:
420
+ if video_url:
421
+ progress_bar = st.progress(0)
422
+ status_text = st.empty()
423
+
424
+ downloaded_file = download_video(video_url, progress_bar, status_text)
425
+
426
+ if downloaded_file and os.path.exists(downloaded_file):
427
+ with open(downloaded_file, 'rb') as file:
428
+ st.download_button(
429
+ label="โฌ‡๏ธ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ",
430
+ data=file,
431
+ file_name=os.path.basename(downloaded_file),
432
+ mime="video/mp4",
433
+ use_container_width=True
434
+ )
435
+ else:
436
+ st.warning("โš ๏ธ YouTube URL์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
437
+
438
+ # ์ดˆ๊ธฐํ™” ๋กœ์ง
439
+ if reset_button:
440
+ st.session_state.video_url = ""
441
+ st.rerun() # st.experimental_rerun()์„ st.rerun()์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.
442