lcjln commited on
Commit
64a29d6
Β·
verified Β·
1 Parent(s): ae6afa3

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +126 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import yt_dlp
3
+ import os
4
+
5
+ # Streamlit 제λͺ©
6
+ st.title('유튜브 μ˜μƒ λ‹€μš΄λ‘œλ“œ')
7
+ st.write("<br>", unsafe_allow_html=True) # 1쀄 띄어쓰기
8
+ st.write("μ‚¬μš©λ°©λ²•")
9
+ st.write("<br>", unsafe_allow_html=True) # 1쀄 띄어쓰기
10
+ st.write("1. λ‹€μš΄λ°›μœΌλ €λŠ” 유튜브 μ˜μƒμ˜ 링크λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”")
11
+ st.write("2. 링크λ₯Ό μž…λ ₯ν•˜κ³  ν™•μΈλ²„νŠΌμ„ λˆ„λ₯΄λ©΄ ν•΄λ‹Ή μ˜μƒμ˜ 썸넀일이 λ“±μž₯ν•©λ‹ˆλ‹€")
12
+ st.write("3. 썸넀일 λ°‘μ˜ μ„ νƒλž€μ„ 톡해 해상도와 μ˜μƒ μš©λŸ‰μ„ ν™•μΈν•œ ν›„ μ›ν•˜λŠ” 해상도λ₯Ό μ„ νƒν•˜κ³  μΆ”μΆœ λ²„νŠΌμ„ λˆŒλŸ¬μ£Όμ„Έμš”")
13
+ st.write("4. μž μ‹œ ν›„ λ‹€μš΄λ°›μœΌλ €λŠ” μ˜μƒμ΄ 밑에 λ“±μž₯ν•˜κ³  μž¬μƒν•˜κ±°λ‚˜ λ‹€μš΄λ‘œλ“œ 받을 수 μžˆμŠ΅λ‹ˆλ‹€ (μ˜μƒμ˜ μš©λŸ‰μ΄ 클수둝 μΆ”μΆœν•˜λŠ”λ° μ‹œκ°„μ΄ 더 κ±Έλ¦½λ‹ˆλ‹€. 20λΆ„ μ˜μƒ μΆ”μΆœν•˜λŠ”λ° 1λΆ„λ‚΄λ‘œ μ†Œμš”)")
14
+
15
+ # μœ μ €λ‘œλΆ€ν„° 유튜브 링크 μž…λ ₯λ°›κΈ°
16
+ col1, col2 = st.columns([4, 1])
17
+ with col1:
18
+ youtube_url = st.text_input('Enter YouTube video URL')
19
+
20
+ def fetch_video_info(youtube_url):
21
+ ydl_opts = {'nocheckcertificate': True}
22
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
23
+ info_dict = ydl.extract_info(youtube_url, download=False)
24
+ return info_dict
25
+
26
+ def get_available_formats(info_dict):
27
+ formats = {}
28
+ for fmt in info_dict['formats']:
29
+ if fmt.get('height') and fmt.get('ext') == 'mp4':
30
+ filesize = fmt.get('filesize', 0)
31
+ if filesize:
32
+ filesize_mb = round(filesize / (1024 * 1024), 2)
33
+ resolution = f"{fmt['height']}p"
34
+ formats[resolution] = {
35
+ 'id': fmt['format_id'],
36
+ 'info': f"{resolution} - {filesize_mb} MB"
37
+ }
38
+ return formats
39
+
40
+ def reset_session_state():
41
+ st.session_state.pop('info_dict', None)
42
+ st.session_state.pop('thumbnail_url', None)
43
+ st.session_state.pop('formats', None)
44
+ st.session_state.pop('youtube_url', None)
45
+ st.session_state.pop('confirmed', None)
46
+ st.session_state.pop('selected_format_id', None)
47
+
48
+ def delete_downloaded_video():
49
+ if os.path.exists('downloaded_video.mp4'):
50
+ os.remove('downloaded_video.mp4')
51
+
52
+ # "확인" λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ μ²˜λ¦¬ν•  둜직
53
+ if st.button('확인'):
54
+ if youtube_url:
55
+ try:
56
+ # κΈ°μ‘΄ λ‹€μš΄λ‘œλ“œλœ μ˜μƒ μ‚­μ œ
57
+ delete_downloaded_video()
58
+
59
+ # μ„Έμ…˜ μƒνƒœ μ΄ˆκΈ°ν™” 및 URL λ³€κ²½ 확인
60
+ if 'youtube_url' in st.session_state and st.session_state.youtube_url != youtube_url:
61
+ reset_session_state()
62
+
63
+ st.session_state.youtube_url = youtube_url
64
+ info_dict = fetch_video_info(youtube_url)
65
+ st.session_state.info_dict = info_dict
66
+ st.session_state.thumbnail_url = info_dict.get('thumbnail')
67
+ st.session_state.formats = get_available_formats(info_dict)
68
+ st.session_state.confirmed = True
69
+ except Exception as e:
70
+ st.error(f"An error occurred: {e}")
71
+ else:
72
+ st.warning('Please enter a valid YouTube video URL')
73
+
74
+ if 'confirmed' in st.session_state and st.session_state.confirmed:
75
+ st.image(st.session_state.thumbnail_url, caption='Video Thumbnail', use_column_width=True)
76
+
77
+ # 해상도 선택
78
+ format_options = [value['info'] for value in st.session_state.formats.values()]
79
+ format_selection = st.selectbox('Select resolution', format_options)
80
+
81
+ # μ„ νƒλœ ν•΄μƒλ„μ˜ format_id μ €μž₯
82
+ st.session_state.selected_format_id = next(
83
+ (value['id'] for key, value in st.session_state.formats.items() if value['info'] == format_selection), None
84
+ )
85
+
86
+ def download_video(youtube_url, format_id):
87
+ ydl_opts = {
88
+ 'format': f"{format_id}+bestaudio/best",
89
+ 'merge_output_format': 'mp4',
90
+ 'outtmpl': 'downloaded_video.%(ext)s',
91
+ 'nocache': True, # μΊμ‹œ λΉ„ν™œμ„±ν™”
92
+ 'force_generic_extractor': True # 항상 μƒˆλ‘œμš΄ URL μ‚¬μš©
93
+ }
94
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
95
+ ydl.download([youtube_url])
96
+
97
+ # "μΆ”μΆœ" λ²„νŠΌ μΆ”κ°€
98
+ if st.button('μΆ”μΆœ'):
99
+ format_id = st.session_state.selected_format_id
100
+ if format_id is None:
101
+ st.error('Selected format not found.')
102
+ else:
103
+ try:
104
+ # μ„Έμ…˜ μƒνƒœμ˜ youtube_url μ‚¬μš©
105
+ download_video(st.session_state.youtube_url, format_id)
106
+ st.success('Video downloaded successfully!')
107
+ st.video('downloaded_video.mp4')
108
+
109
+ # λ‹€μš΄λ‘œλ“œλœ 파일 제곡 (optional)
110
+ with open('downloaded_video.mp4', 'rb') as file:
111
+ st.download_button(
112
+ label="Download Video File",
113
+ data=file,
114
+ file_name='downloaded_video.mp4',
115
+ mime='video/mp4'
116
+ )
117
+
118
+ # μΆ”μΆœλœ μ˜μƒ 정보 좜λ ₯
119
+ st.write(f"**제λͺ©:** {st.session_state.info_dict['title']}")
120
+ st.write(f"**채널λͺ…:** {st.session_state.info_dict['uploader']}")
121
+ st.write(f"**μ—…λ‘œλ“œ λ‚ μ§œ:** {st.session_state.info_dict['upload_date']}")
122
+
123
+ # λ‹€μš΄λ‘œλ“œ ν›„ μ„Έμ…˜ μƒνƒœ 리셋
124
+ reset_session_state()
125
+ except Exception as e:
126
+ st.error(f"An error occurred during download: {e}")
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ streamlit
2
+ yt-dlp
3
+ moviepy