Spaces:
Runtime error
Runtime error
APIs updated & yt-dlp added
Browse files- helperfunctions.py +108 -0
- languages.py +411 -0
- main.py +131 -37
- media_download.py +319 -212
- summarizer.py +8 -8
- temp/translated_subtitles.json +227 -0
- temp/translated_transcript.txt +1 -0
- translation.py +127 -0
helperfunctions.py
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import subprocess
|
3 |
+
|
4 |
+
|
5 |
+
def extract_audio(video_path):
|
6 |
+
"""
|
7 |
+
Extract audio from a video file (MP4 or WebM) and save it as an MP3 file using ffmpeg.
|
8 |
+
|
9 |
+
Args:
|
10 |
+
video_path (str): Path to the input video file.
|
11 |
+
|
12 |
+
Returns:
|
13 |
+
str: Path of extracted audio.
|
14 |
+
"""
|
15 |
+
try:
|
16 |
+
# Path for Extracted Audio File
|
17 |
+
filename, extension = os.path.splitext(video_path)
|
18 |
+
audio_path = filename + '.mp3'
|
19 |
+
|
20 |
+
# Choosing the Appropriate Codec for the Output Audio Format (MP3)
|
21 |
+
audio_codec = "libmp3lame" if extension.lower() in (".mp4", ".webm") else "mp3"
|
22 |
+
|
23 |
+
# Extracting Audio using FFMPEG Command
|
24 |
+
command = ["ffmpeg", "-i", video_path, "-vn", "-acodec",
|
25 |
+
audio_codec, audio_path, '-loglevel', 'quiet']
|
26 |
+
subprocess.run(command, check=True)
|
27 |
+
|
28 |
+
return audio_path
|
29 |
+
|
30 |
+
except Exception as e:
|
31 |
+
print(f"Error in extract_audio: {e}")
|
32 |
+
|
33 |
+
def burn_subtitles(video_file_path, subtitle_file_path):
|
34 |
+
'''
|
35 |
+
Burns the subtitles onto the video
|
36 |
+
|
37 |
+
Args:
|
38 |
+
video_file_path (str): Path to the input video file.
|
39 |
+
subtitle_file_path (str): Path to the subtitle file.
|
40 |
+
|
41 |
+
Returns:
|
42 |
+
str: Path of output video with subtitles.
|
43 |
+
'''
|
44 |
+
try:
|
45 |
+
# Getting Output File Path
|
46 |
+
video_filename, video_extension = os.path.splitext(video_file_path)
|
47 |
+
subtitle_filename, subtitle_extension = os.path.splitext(subtitle_file_path)
|
48 |
+
output_file_path = video_filename + subtitle_extension.replace('.', '_') + video_extension
|
49 |
+
|
50 |
+
# Burning the Subtitles onto Video using FFMPEG Command
|
51 |
+
command = ['ffmpeg', '-i', video_file_path,
|
52 |
+
'-vf', f'subtitles={subtitle_file_path}',
|
53 |
+
output_file_path, '-loglevel', 'quiet']
|
54 |
+
subprocess.run(command, check=True)
|
55 |
+
|
56 |
+
return output_file_path
|
57 |
+
|
58 |
+
except Exception as e:
|
59 |
+
print(f"Error in burn_subtitles: {e}")
|
60 |
+
|
61 |
+
def convert_to_srt_time_format(seconds):
|
62 |
+
'''
|
63 |
+
Converts seconds into .srt format
|
64 |
+
'''
|
65 |
+
|
66 |
+
try:
|
67 |
+
hours = seconds // 3600
|
68 |
+
seconds %= 3600
|
69 |
+
minutes = seconds // 60
|
70 |
+
seconds %= 60
|
71 |
+
milliseconds = int((seconds - int(seconds)) * 1000)
|
72 |
+
return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d},{milliseconds:03d}"
|
73 |
+
|
74 |
+
except Exception as e:
|
75 |
+
print(f"Error in save_translated_subtitles: {e}")
|
76 |
+
|
77 |
+
def save_translated_subtitles(subtitles, media_path):
|
78 |
+
'''
|
79 |
+
Saves the translated subtitles into .srt file
|
80 |
+
'''
|
81 |
+
|
82 |
+
try:
|
83 |
+
# Converting to SRT Format
|
84 |
+
srt_content = ""
|
85 |
+
counter = 1
|
86 |
+
for subtitle in subtitles:
|
87 |
+
start_time = subtitle['start']
|
88 |
+
end_time = subtitle['end']
|
89 |
+
text = subtitle['text']
|
90 |
+
|
91 |
+
srt_content += f"{counter}\n"
|
92 |
+
srt_content += f"{convert_to_srt_time_format(start_time)} --> {convert_to_srt_time_format(end_time)}\n"
|
93 |
+
srt_content += f"{text}\n\n"
|
94 |
+
|
95 |
+
counter += 1
|
96 |
+
|
97 |
+
# Saving SRT content to a .srt file
|
98 |
+
print(media_path)
|
99 |
+
subtitles_filename = os.path.splitext(media_path)[0]
|
100 |
+
subtitles_filename = f'{subtitles_filename}.srt'
|
101 |
+
print(subtitles_filename)
|
102 |
+
with open(subtitles_filename, 'w', encoding='utf-8') as srt_file:
|
103 |
+
srt_file.write(srt_content)
|
104 |
+
|
105 |
+
return subtitles_filename
|
106 |
+
|
107 |
+
except Exception as e:
|
108 |
+
print(f"Error in save_translated_subtitles: {e}")
|
languages.py
ADDED
@@ -0,0 +1,411 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# OpenAI Whisper - Supported Languages
|
2 |
+
whisper_languages = [
|
3 |
+
'afrikaans',
|
4 |
+
'arabic',
|
5 |
+
'armenian',
|
6 |
+
'azerbaijani',
|
7 |
+
'belarusian',
|
8 |
+
'bosnian',
|
9 |
+
'bulgarian',
|
10 |
+
'catalan',
|
11 |
+
'chinese',
|
12 |
+
'croatian',
|
13 |
+
'czech',
|
14 |
+
'danish',
|
15 |
+
'dutch',
|
16 |
+
'english',
|
17 |
+
'estonian',
|
18 |
+
'finnish',
|
19 |
+
'french',
|
20 |
+
'galician',
|
21 |
+
'german',
|
22 |
+
'greek',
|
23 |
+
'hebrew',
|
24 |
+
'hindi',
|
25 |
+
'hungarian',
|
26 |
+
'icelandic',
|
27 |
+
'indonesian',
|
28 |
+
'italian',
|
29 |
+
'japanese',
|
30 |
+
'kannada',
|
31 |
+
'kazakh',
|
32 |
+
'korean',
|
33 |
+
'latvian',
|
34 |
+
'lithuanian',
|
35 |
+
'macedonian',
|
36 |
+
'malay',
|
37 |
+
'marathi',
|
38 |
+
'maori',
|
39 |
+
'nepali',
|
40 |
+
'norwegian',
|
41 |
+
'persian',
|
42 |
+
'polish',
|
43 |
+
'portuguese',
|
44 |
+
'romanian',
|
45 |
+
'russian',
|
46 |
+
'serbian',
|
47 |
+
'slovak',
|
48 |
+
'slovenian',
|
49 |
+
'spanish',
|
50 |
+
'swahili',
|
51 |
+
'swedish',
|
52 |
+
'tagalog',
|
53 |
+
'tamil',
|
54 |
+
'thai',
|
55 |
+
'turkish',
|
56 |
+
'ukrainian',
|
57 |
+
'urdu',
|
58 |
+
'vietnamese',
|
59 |
+
'welsh'
|
60 |
+
]
|
61 |
+
|
62 |
+
# Google Translate - Supported Languages
|
63 |
+
gt_languages = {
|
64 |
+
'afrikaans': 'af',
|
65 |
+
'albanian': 'sq',
|
66 |
+
'amharic': 'am',
|
67 |
+
'arabic': 'ar',
|
68 |
+
'armenian': 'hy',
|
69 |
+
'assamese': 'as',
|
70 |
+
'aymara': 'ay',
|
71 |
+
'azerbaijani': 'az',
|
72 |
+
'bambara': 'bm',
|
73 |
+
'basque': 'eu',
|
74 |
+
'belarusian': 'be',
|
75 |
+
'bengali': 'bn',
|
76 |
+
'bhojpuri': 'bho',
|
77 |
+
'bosnian': 'bs',
|
78 |
+
'bulgarian': 'bg',
|
79 |
+
'catalan': 'ca',
|
80 |
+
'cebuano': 'ceb',
|
81 |
+
'chichewa': 'ny',
|
82 |
+
'chinese (simplified)': 'zh-CN',
|
83 |
+
'chinese (traditional)': 'zh-TW',
|
84 |
+
'corsican': 'co',
|
85 |
+
'croatian': 'hr',
|
86 |
+
'czech': 'cs',
|
87 |
+
'danish': 'da',
|
88 |
+
'dhivehi': 'dv',
|
89 |
+
'dogri': 'doi',
|
90 |
+
'dutch': 'nl',
|
91 |
+
'english': 'en',
|
92 |
+
'esperanto': 'eo',
|
93 |
+
'estonian': 'et',
|
94 |
+
'ewe': 'ee',
|
95 |
+
'filipino': 'tl',
|
96 |
+
'finnish': 'fi',
|
97 |
+
'french': 'fr',
|
98 |
+
'frisian': 'fy',
|
99 |
+
'galician': 'gl',
|
100 |
+
'georgian': 'ka',
|
101 |
+
'german': 'de',
|
102 |
+
'greek': 'el',
|
103 |
+
'guarani': 'gn',
|
104 |
+
'gujarati': 'gu',
|
105 |
+
'haitian creole': 'ht',
|
106 |
+
'hausa': 'ha',
|
107 |
+
'hawaiian': 'haw',
|
108 |
+
'hebrew': 'iw',
|
109 |
+
'hindi': 'hi',
|
110 |
+
'hmong': 'hmn',
|
111 |
+
'hungarian': 'hu',
|
112 |
+
'icelandic': 'is',
|
113 |
+
'igbo': 'ig',
|
114 |
+
'ilocano': 'ilo',
|
115 |
+
'indonesian': 'id',
|
116 |
+
'irish': 'ga',
|
117 |
+
'italian': 'it',
|
118 |
+
'japanese': 'ja',
|
119 |
+
'javanese': 'jw',
|
120 |
+
'kannada': 'kn',
|
121 |
+
'kazakh': 'kk',
|
122 |
+
'khmer': 'km',
|
123 |
+
'kinyarwanda': 'rw',
|
124 |
+
'konkani': 'gom',
|
125 |
+
'korean': 'ko',
|
126 |
+
'krio': 'kri',
|
127 |
+
'kurdish (kurmanji)': 'ku',
|
128 |
+
'kurdish (sorani)': 'ckb',
|
129 |
+
'kyrgyz': 'ky',
|
130 |
+
'lao': 'lo',
|
131 |
+
'latin': 'la',
|
132 |
+
'latvian': 'lv',
|
133 |
+
'lingala': 'ln',
|
134 |
+
'lithuanian': 'lt',
|
135 |
+
'luganda': 'lg',
|
136 |
+
'luxembourgish': 'lb',
|
137 |
+
'macedonian': 'mk',
|
138 |
+
'maithili': 'mai',
|
139 |
+
'malagasy': 'mg',
|
140 |
+
'malay': 'ms',
|
141 |
+
'malayalam': 'ml',
|
142 |
+
'maltese': 'mt',
|
143 |
+
'maori': 'mi',
|
144 |
+
'marathi': 'mr',
|
145 |
+
'meiteilon (manipuri)': 'mni-Mtei',
|
146 |
+
'mizo': 'lus',
|
147 |
+
'mongolian': 'mn',
|
148 |
+
'myanmar': 'my',
|
149 |
+
'nepali': 'ne',
|
150 |
+
'norwegian': 'no',
|
151 |
+
'odia (oriya)': 'or',
|
152 |
+
'oromo': 'om',
|
153 |
+
'pashto': 'ps',
|
154 |
+
'persian': 'fa',
|
155 |
+
'polish': 'pl',
|
156 |
+
'portuguese': 'pt',
|
157 |
+
'punjabi': 'pa',
|
158 |
+
'quechua': 'qu',
|
159 |
+
'romanian': 'ro',
|
160 |
+
'russian': 'ru',
|
161 |
+
'samoan': 'sm',
|
162 |
+
'sanskrit': 'sa',
|
163 |
+
'scots gaelic': 'gd',
|
164 |
+
'sepedi': 'nso',
|
165 |
+
'serbian': 'sr',
|
166 |
+
'sesotho': 'st',
|
167 |
+
'shona': 'sn',
|
168 |
+
'sindhi': 'sd',
|
169 |
+
'sinhala': 'si',
|
170 |
+
'slovak': 'sk',
|
171 |
+
'slovenian': 'sl',
|
172 |
+
'somali': 'so',
|
173 |
+
'spanish': 'es',
|
174 |
+
'sundanese': 'su',
|
175 |
+
'swahili': 'sw',
|
176 |
+
'swedish': 'sv',
|
177 |
+
'tajik': 'tg',
|
178 |
+
'tamil': 'ta',
|
179 |
+
'tatar': 'tt',
|
180 |
+
'telugu': 'te',
|
181 |
+
'thai': 'th',
|
182 |
+
'tigrinya': 'ti',
|
183 |
+
'tsonga': 'ts',
|
184 |
+
'turkish': 'tr',
|
185 |
+
'turkmen': 'tk',
|
186 |
+
'twi': 'ak',
|
187 |
+
'ukrainian': 'uk',
|
188 |
+
'urdu': 'ur',
|
189 |
+
'uyghur': 'ug',
|
190 |
+
'uzbek': 'uz',
|
191 |
+
'vietnamese': 'vi',
|
192 |
+
'welsh': 'cy',
|
193 |
+
'xhosa': 'xh',
|
194 |
+
'yiddish': 'yi',
|
195 |
+
'yoruba': 'yo',
|
196 |
+
'zulu': 'zu'
|
197 |
+
}
|
198 |
+
|
199 |
+
# NLLB - Supported Languages
|
200 |
+
nllb_languages = [
|
201 |
+
'Acehnese (Arabic script)',
|
202 |
+
'Acehnese (Latin script)',
|
203 |
+
'Afrikaans',
|
204 |
+
'Akan',
|
205 |
+
'Amharic',
|
206 |
+
'Armenian',
|
207 |
+
'Assamese',
|
208 |
+
'Asturian',
|
209 |
+
'Awadhi',
|
210 |
+
'Ayacucho Quechua',
|
211 |
+
'Balinese',
|
212 |
+
'Bambara',
|
213 |
+
'Banjar (Arabic script)',
|
214 |
+
'Banjar (Latin script)',
|
215 |
+
'Bashkir',
|
216 |
+
'Basque',
|
217 |
+
'Belarusian',
|
218 |
+
'Bemba',
|
219 |
+
'Bengali',
|
220 |
+
'Bhojpuri',
|
221 |
+
'Bosnian',
|
222 |
+
'Buginese',
|
223 |
+
'Bulgarian',
|
224 |
+
'Burmese',
|
225 |
+
'Catalan',
|
226 |
+
'Cebuano',
|
227 |
+
'Central Atlas Tamazight',
|
228 |
+
'Central Aymara',
|
229 |
+
'Central Kanuri (Arabic script)',
|
230 |
+
'Central Kanuri (Latin script)',
|
231 |
+
'Central Kurdish',
|
232 |
+
'Chhattisgarhi',
|
233 |
+
'Chinese (Simplified)',
|
234 |
+
'Chinese (Traditional)',
|
235 |
+
'Chokwe',
|
236 |
+
'Crimean Tatar',
|
237 |
+
'Croatian',
|
238 |
+
'Czech',
|
239 |
+
'Danish',
|
240 |
+
'Dari',
|
241 |
+
'Dutch',
|
242 |
+
'Dyula',
|
243 |
+
'Dzongkha',
|
244 |
+
'Eastern Panjabi',
|
245 |
+
'Eastern Yiddish',
|
246 |
+
'Egyptian Arabic',
|
247 |
+
'English',
|
248 |
+
'Esperanto',
|
249 |
+
'Estonian',
|
250 |
+
'Ewe',
|
251 |
+
'Faroese',
|
252 |
+
'Fijian',
|
253 |
+
'Finnish',
|
254 |
+
'Fon',
|
255 |
+
'French',
|
256 |
+
'Friulian',
|
257 |
+
'Galician',
|
258 |
+
'Ganda',
|
259 |
+
'Georgian',
|
260 |
+
'German',
|
261 |
+
'Greek',
|
262 |
+
'Guarani',
|
263 |
+
'Gujarati',
|
264 |
+
'Haitian Creole',
|
265 |
+
'Halh Mongolian',
|
266 |
+
'Hausa',
|
267 |
+
'Hebrew',
|
268 |
+
'Hindi',
|
269 |
+
'Hungarian',
|
270 |
+
'Icelandic',
|
271 |
+
'Igbo',
|
272 |
+
'Ilocano',
|
273 |
+
'Indonesian',
|
274 |
+
'Irish',
|
275 |
+
'Italian',
|
276 |
+
'Japanese',
|
277 |
+
'Javanese',
|
278 |
+
'Jingpho',
|
279 |
+
'Kabiyè',
|
280 |
+
'Kabuverdianu',
|
281 |
+
'Kabyle',
|
282 |
+
'Kamba',
|
283 |
+
'Kannada',
|
284 |
+
'Kashmiri (Arabic script)',
|
285 |
+
'Kashmiri (Devanagari script)',
|
286 |
+
'Kazakh',
|
287 |
+
'Khmer',
|
288 |
+
'Kikongo',
|
289 |
+
'Kikuyu',
|
290 |
+
'Kimbundu',
|
291 |
+
'Kinyarwanda',
|
292 |
+
'Korean',
|
293 |
+
'Kyrgyz',
|
294 |
+
'Lao',
|
295 |
+
'Latgalian',
|
296 |
+
'Ligurian',
|
297 |
+
'Limburgish',
|
298 |
+
'Lingala',
|
299 |
+
'Lithuanian',
|
300 |
+
'Lombard',
|
301 |
+
'Luba-Kasai',
|
302 |
+
'Luo',
|
303 |
+
'Luxembourgish',
|
304 |
+
'Macedonian',
|
305 |
+
'Magahi',
|
306 |
+
'Maithili',
|
307 |
+
'Malayalam',
|
308 |
+
'Maltese',
|
309 |
+
'Maori',
|
310 |
+
'Marathi',
|
311 |
+
'Meitei (Bengali script)',
|
312 |
+
'Mesopotamian Arabic',
|
313 |
+
'Minangkabau (Arabic script)',
|
314 |
+
'Minangkabau (Latin script)',
|
315 |
+
'Mizo',
|
316 |
+
'Modern Standard Arabic',
|
317 |
+
'Modern Standard Arabic (Romanized)',
|
318 |
+
'Moroccan Arabic',
|
319 |
+
'Mossi',
|
320 |
+
'Najdi Arabic',
|
321 |
+
'Nepali',
|
322 |
+
'Nigerian Fulfulde',
|
323 |
+
'North Azerbaijani',
|
324 |
+
'North Levantine Arabic',
|
325 |
+
'Northern Kurdish',
|
326 |
+
'Northern Sotho',
|
327 |
+
'Northern Uzbek',
|
328 |
+
'Norwegian Bokmål',
|
329 |
+
'Norwegian Nynorsk',
|
330 |
+
'Nuer',
|
331 |
+
'Nyanja',
|
332 |
+
'Occitan',
|
333 |
+
'Odia',
|
334 |
+
'Pangasinan',
|
335 |
+
'Papiamento',
|
336 |
+
'Plateau Malagasy',
|
337 |
+
'Polish',
|
338 |
+
'Portuguese',
|
339 |
+
'Romanian',
|
340 |
+
'Rundi',
|
341 |
+
'Russian',
|
342 |
+
'Samoan',
|
343 |
+
'Sango',
|
344 |
+
'Sanskrit',
|
345 |
+
'Santali',
|
346 |
+
'Sardinian',
|
347 |
+
'Scottish Gaelic',
|
348 |
+
'Serbian',
|
349 |
+
'Shan',
|
350 |
+
'Shona',
|
351 |
+
'Sicilian',
|
352 |
+
'Silesian',
|
353 |
+
'Sindhi',
|
354 |
+
'Sinhala',
|
355 |
+
'Slovak',
|
356 |
+
'Slovenian',
|
357 |
+
'Somali',
|
358 |
+
'South Azerbaijani',
|
359 |
+
'South Levantine Arabic',
|
360 |
+
'Southern Pashto',
|
361 |
+
'Southern Sotho',
|
362 |
+
'Southwestern Dinka',
|
363 |
+
'Spanish',
|
364 |
+
'Standard Latvian',
|
365 |
+
'Standard Malay',
|
366 |
+
'Standard Tibetan',
|
367 |
+
'Sundanese',
|
368 |
+
'Swahili',
|
369 |
+
'Swati',
|
370 |
+
'Swedish',
|
371 |
+
"Ta'izzi-Adeni Arabic",
|
372 |
+
'Tagalog',
|
373 |
+
'Tajik',
|
374 |
+
'Tamasheq (Latin script)',
|
375 |
+
'Tamasheq (Tifinagh script)',
|
376 |
+
'Tamil',
|
377 |
+
'Tatar',
|
378 |
+
'Telugu',
|
379 |
+
'Thai',
|
380 |
+
'Tigrinya',
|
381 |
+
'Tok Pisin',
|
382 |
+
'Tosk Albanian',
|
383 |
+
'Tsonga',
|
384 |
+
'Tswana',
|
385 |
+
'Tumbuka',
|
386 |
+
'Tunisian Arabic',
|
387 |
+
'Turkish',
|
388 |
+
'Turkmen',
|
389 |
+
'Twi',
|
390 |
+
'Ukrainian',
|
391 |
+
'Umbundu',
|
392 |
+
'Urdu',
|
393 |
+
'Uyghur',
|
394 |
+
'Venetian',
|
395 |
+
'Vietnamese',
|
396 |
+
'Waray',
|
397 |
+
'Welsh',
|
398 |
+
'West Central Oromo',
|
399 |
+
'Western Persian',
|
400 |
+
'Wolof',
|
401 |
+
'Xhosa',
|
402 |
+
'Yoruba',
|
403 |
+
'Yue Chinese',
|
404 |
+
'Zulu'
|
405 |
+
]
|
406 |
+
|
407 |
+
LANGUAGES = {}
|
408 |
+
for lang in nllb_languages:
|
409 |
+
lang_code = gt_languages.get(lang.lower(), None)
|
410 |
+
if lang_code:
|
411 |
+
LANGUAGES[lang_code] = lang
|
main.py
CHANGED
@@ -8,9 +8,10 @@ from fastapi import FastAPI, Request
|
|
8 |
from fastapi.middleware.cors import CORSMiddleware
|
9 |
|
10 |
from media_download import YoutubeDownloader
|
11 |
-
from transcription import StableWhisper
|
12 |
-
from summarizer import Extract_Summary, AudioBookNarration
|
13 |
-
from audiobook import AudioBook
|
|
|
14 |
|
15 |
|
16 |
### API Configurations
|
@@ -23,25 +24,25 @@ output_folder = 'Output'
|
|
23 |
# Create a context variable to store the contexts for each user
|
24 |
users_context = dict()
|
25 |
|
26 |
-
# CORS (Cross-Origin Resource Sharing)
|
27 |
-
origins = [
|
28 |
-
|
29 |
-
|
30 |
-
]
|
31 |
|
32 |
-
app.add_middleware(
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
)
|
39 |
|
40 |
|
41 |
### APIs
|
42 |
|
43 |
-
@app.get("/
|
44 |
-
async def
|
45 |
|
46 |
# Getting User's IP
|
47 |
# user_ip = request.client.host
|
@@ -51,15 +52,39 @@ async def get_media_info(request: Request, url: str):
|
|
51 |
youtube_downloader = YoutubeDownloader(url, output_folder)
|
52 |
|
53 |
# Getting Youtube Media Info
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
-
#
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
|
|
61 |
|
62 |
-
return
|
63 |
|
64 |
|
65 |
@app.get("/download_media")
|
@@ -80,11 +105,11 @@ async def download_media(request: Request, media_type: str, media_format: str, m
|
|
80 |
users_context[user_ip]['media_path'] = media_path
|
81 |
users_context[user_ip]['media_type'] = media_type
|
82 |
|
83 |
-
return {
|
84 |
|
85 |
|
86 |
@app.get("/get_transcript")
|
87 |
-
async def get_transcript(request: Request, subtitle_format: str = 'srt', word_level: bool =
|
88 |
|
89 |
# Getting User's IP
|
90 |
# user_ip = request.client.host
|
@@ -96,7 +121,7 @@ async def get_transcript(request: Request, subtitle_format: str = 'srt', word_le
|
|
96 |
# Checking if the media_type is Video, then extract it's audio
|
97 |
media_type = users_context[user_ip]['media_type']
|
98 |
if media_type == 'video':
|
99 |
-
media_path =
|
100 |
|
101 |
# # Whisper based transcription
|
102 |
# stable_whisper_transcript = StableWhisper(media_path, output_folder, subtitle_format=subtitle_format, word_level=word_level)
|
@@ -122,7 +147,44 @@ async def get_transcript(request: Request, subtitle_format: str = 'srt', word_le
|
|
122 |
users_context[user_ip]['transcript'] = transcript
|
123 |
users_context[user_ip]['transcript_path'] = transcript_path
|
124 |
|
125 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
|
128 |
@app.get("/get_summary")
|
@@ -159,7 +221,7 @@ async def get_summary(request: Request, Summary_type: str, Summary_strategy: str
|
|
159 |
# Storing Summary Info in the context for this user's session
|
160 |
users_context[user_ip]['summary'] = output
|
161 |
|
162 |
-
return {
|
163 |
|
164 |
|
165 |
@app.get("/get_key_info")
|
@@ -196,11 +258,11 @@ async def get_key_info(request: Request, Summary_type: str, Summary_strategy: st
|
|
196 |
# Storing Key Info in the context for this user's session
|
197 |
users_context[user_ip]['key_info'] = output
|
198 |
|
199 |
-
return {
|
200 |
|
201 |
|
202 |
# @app.get("/get_narration")
|
203 |
-
# async def get_narration(request: Request,
|
204 |
|
205 |
# # Getting User's IP
|
206 |
# # user_ip = request.client.host
|
@@ -212,7 +274,7 @@ async def get_key_info(request: Request, Summary_type: str, Summary_strategy: st
|
|
212 |
|
213 |
# # # Extracting Narration
|
214 |
# # narrator = AudioBookNarration(text_input=text_input)
|
215 |
-
# # output = narrator.define_chain(
|
216 |
|
217 |
# temp_dir = 'temp'
|
218 |
# file_path = os.path.join(temp_dir, 'narration.txt')
|
@@ -227,11 +289,11 @@ async def get_key_info(request: Request, Summary_type: str, Summary_strategy: st
|
|
227 |
# # Storing Narration Info in the context for this user's session
|
228 |
# users_context[user_ip]['narration'] = output
|
229 |
|
230 |
-
# return {
|
231 |
|
232 |
|
233 |
-
@app.get("/
|
234 |
-
async def get_audiobook(request: Request,
|
235 |
|
236 |
# Getting User's IP
|
237 |
# user_ip = request.client.host
|
@@ -244,7 +306,7 @@ async def get_audiobook(request: Request,Narration_style: str, output_type : str
|
|
244 |
# # Extracting Narration
|
245 |
|
246 |
# narrator = AudioBookNarration(text_input=text_input)
|
247 |
-
# output = narrator.define_chain(
|
248 |
|
249 |
# # Generating Audiobook
|
250 |
# audiobook = AudioBook(output_folder=output_folder)
|
@@ -262,7 +324,39 @@ async def get_audiobook(request: Request,Narration_style: str, output_type : str
|
|
262 |
# Storing Audiobook path in the context for this user's session
|
263 |
users_context[user_ip]['audiobook_path'] = audio_path
|
264 |
|
265 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
|
267 |
|
268 |
if __name__ == "__main__":
|
|
|
8 |
from fastapi.middleware.cors import CORSMiddleware
|
9 |
|
10 |
from media_download import YoutubeDownloader
|
11 |
+
# from transcription import StableWhisper
|
12 |
+
# from summarizer import Extract_Summary, AudioBookNarration
|
13 |
+
# from audiobook import AudioBook
|
14 |
+
from helperfunctions import *
|
15 |
|
16 |
|
17 |
### API Configurations
|
|
|
24 |
# Create a context variable to store the contexts for each user
|
25 |
users_context = dict()
|
26 |
|
27 |
+
# # CORS (Cross-Origin Resource Sharing)
|
28 |
+
# origins = [
|
29 |
+
# "http://localhost",
|
30 |
+
# "http://localhost:4200",
|
31 |
+
# ]
|
32 |
|
33 |
+
# app.add_middleware(
|
34 |
+
# CORSMiddleware,
|
35 |
+
# allow_origins=["*"], # origins,
|
36 |
+
# allow_credentials=True,
|
37 |
+
# allow_methods=["*"],
|
38 |
+
# allow_headers=["*"],
|
39 |
+
# )
|
40 |
|
41 |
|
42 |
### APIs
|
43 |
|
44 |
+
@app.get("/get_media_metadata")
|
45 |
+
async def get_media_metadata(request: Request, url: str):
|
46 |
|
47 |
# Getting User's IP
|
48 |
# user_ip = request.client.host
|
|
|
52 |
youtube_downloader = YoutubeDownloader(url, output_folder)
|
53 |
|
54 |
# Getting Youtube Media Info
|
55 |
+
media_metadata = youtube_downloader.get_media_metadata()
|
56 |
+
|
57 |
+
# Getting Status
|
58 |
+
status = 1 if media_metadata else 0
|
59 |
+
|
60 |
+
if status:
|
61 |
+
# Storing Info in the context for this user's session
|
62 |
+
users_context[user_ip] = dict()
|
63 |
+
users_context[user_ip]['downloader'] = youtube_downloader
|
64 |
+
# users_context[user_ip]['media_metadata'] = media_metadata
|
65 |
+
users_context[user_ip]['url'] = url
|
66 |
+
|
67 |
+
return {'status': status, 'media_metadata': media_metadata}
|
68 |
+
|
69 |
+
|
70 |
+
@app.get("/get_media_formats")
|
71 |
+
async def get_media_formats(request: Request):
|
72 |
+
|
73 |
+
# Getting User's IP
|
74 |
+
# user_ip = request.client.host
|
75 |
+
user_ip = 1
|
76 |
+
|
77 |
+
# Downloading Media for User
|
78 |
+
media_formats = users_context[user_ip]['downloader'].get_media_formats()
|
79 |
|
80 |
+
# Getting Status
|
81 |
+
status = 1 if media_formats else 0
|
82 |
+
|
83 |
+
if status:
|
84 |
+
# Storing Media Info in the context for this user's session
|
85 |
+
users_context[user_ip]['media_formats'] = media_formats
|
86 |
|
87 |
+
return {'status': status, 'media_formats': media_formats}
|
88 |
|
89 |
|
90 |
@app.get("/download_media")
|
|
|
105 |
users_context[user_ip]['media_path'] = media_path
|
106 |
users_context[user_ip]['media_type'] = media_type
|
107 |
|
108 |
+
return {'status': status, 'media_path': media_path}
|
109 |
|
110 |
|
111 |
@app.get("/get_transcript")
|
112 |
+
async def get_transcript(request: Request, subtitle_format: str = 'srt', word_level: bool = False):
|
113 |
|
114 |
# Getting User's IP
|
115 |
# user_ip = request.client.host
|
|
|
121 |
# Checking if the media_type is Video, then extract it's audio
|
122 |
media_type = users_context[user_ip]['media_type']
|
123 |
if media_type == 'video':
|
124 |
+
media_path = extract_audio(media_path)
|
125 |
|
126 |
# # Whisper based transcription
|
127 |
# stable_whisper_transcript = StableWhisper(media_path, output_folder, subtitle_format=subtitle_format, word_level=word_level)
|
|
|
147 |
users_context[user_ip]['transcript'] = transcript
|
148 |
users_context[user_ip]['transcript_path'] = transcript_path
|
149 |
|
150 |
+
return {'status': status, "transcript": transcript}
|
151 |
+
|
152 |
+
|
153 |
+
@app.get("/get_translation")
|
154 |
+
async def get_translation(request: Request, target_language: str = 'en'):
|
155 |
+
|
156 |
+
# Getting User's IP
|
157 |
+
# user_ip = request.client.host
|
158 |
+
user_ip = 1
|
159 |
+
|
160 |
+
# Retrieving the transcript from the context for this user's session
|
161 |
+
transcript = users_context[user_ip]['transcript']
|
162 |
+
|
163 |
+
# # # NLLB based Translation
|
164 |
+
# nllb_translator = Translation(transcript, transcript['language'], target_language, 'output_path')
|
165 |
+
# translated_transcript = nllb_translator.get_translated_transcript()
|
166 |
+
# translated_subtitles = nllb_translator.get_translated_subtitles()
|
167 |
+
|
168 |
+
temp_dir = 'temp'
|
169 |
+
|
170 |
+
translated_transcript_path = os.path.join(temp_dir, 'translated_transcript.txt')
|
171 |
+
with open(translated_transcript_path, "r", encoding="utf-8") as f:
|
172 |
+
translated_transcript = f.read()
|
173 |
+
|
174 |
+
translated_subtitles_path = os.path.join(temp_dir, 'translated_subtitles.json')
|
175 |
+
with open(translated_subtitles_path, "r", encoding="utf-8") as json_file:
|
176 |
+
translated_subtitles = json.load(json_file)
|
177 |
+
|
178 |
+
# Getting Status
|
179 |
+
status = 1 if translated_transcript and translated_subtitles else 0
|
180 |
+
|
181 |
+
if status:
|
182 |
+
# Storing Translated Transcript Info in the context for this user's session
|
183 |
+
users_context[user_ip]['translated_transcript'] = translated_transcript
|
184 |
+
users_context[user_ip]['translated_subtitles'] = translated_subtitles
|
185 |
+
# users_context[user_ip]['transcript_path'] = transcript_path
|
186 |
+
|
187 |
+
return {'status': status, "transcript": translated_transcript, "subtitles": translated_subtitles}
|
188 |
|
189 |
|
190 |
@app.get("/get_summary")
|
|
|
221 |
# Storing Summary Info in the context for this user's session
|
222 |
users_context[user_ip]['summary'] = output
|
223 |
|
224 |
+
return {'status': status, "summary": output}
|
225 |
|
226 |
|
227 |
@app.get("/get_key_info")
|
|
|
258 |
# Storing Key Info in the context for this user's session
|
259 |
users_context[user_ip]['key_info'] = output
|
260 |
|
261 |
+
return {'status': status, "key_info": output}
|
262 |
|
263 |
|
264 |
# @app.get("/get_narration")
|
265 |
+
# async def get_narration(request: Request, narration_style: str, text_input: str = None):
|
266 |
|
267 |
# # Getting User's IP
|
268 |
# # user_ip = request.client.host
|
|
|
274 |
|
275 |
# # # Extracting Narration
|
276 |
# # narrator = AudioBookNarration(text_input=text_input)
|
277 |
+
# # output = narrator.define_chain(narration_style=narration_style)
|
278 |
|
279 |
# temp_dir = 'temp'
|
280 |
# file_path = os.path.join(temp_dir, 'narration.txt')
|
|
|
289 |
# # Storing Narration Info in the context for this user's session
|
290 |
# users_context[user_ip]['narration'] = output
|
291 |
|
292 |
+
# return {'status': status, "narration": output}
|
293 |
|
294 |
|
295 |
+
@app.get("/get_audiobook")
|
296 |
+
async def get_audiobook(request: Request, output_type : str, narration_style: str, speaker: str = "male", text_input: str = None):
|
297 |
|
298 |
# Getting User's IP
|
299 |
# user_ip = request.client.host
|
|
|
306 |
# # Extracting Narration
|
307 |
|
308 |
# narrator = AudioBookNarration(text_input=text_input)
|
309 |
+
# output = narrator.define_chain(narration_style=narration_style)
|
310 |
|
311 |
# # Generating Audiobook
|
312 |
# audiobook = AudioBook(output_folder=output_folder)
|
|
|
324 |
# Storing Audiobook path in the context for this user's session
|
325 |
users_context[user_ip]['audiobook_path'] = audio_path
|
326 |
|
327 |
+
return {'status': status, "audiobook_path": audio_path}
|
328 |
+
|
329 |
+
|
330 |
+
@app.get("/get_rendered_video")
|
331 |
+
async def get_rendered_video(request: Request, subtitles_type: str = 'original'):
|
332 |
+
|
333 |
+
# Getting User's IP
|
334 |
+
# user_ip = request.client.host
|
335 |
+
user_ip = 1
|
336 |
+
|
337 |
+
# Retrieving the media_path from the context for this user's session
|
338 |
+
media_path = users_context[user_ip]['media_path']
|
339 |
+
|
340 |
+
# Getting Required Subtitles
|
341 |
+
if subtitles_type == 'original':
|
342 |
+
subtitles_path = users_context[user_ip]['transcript_path']
|
343 |
+
|
344 |
+
elif subtitles_type == 'translated':
|
345 |
+
|
346 |
+
# Getting Translated Subtitles from the context for this user's session
|
347 |
+
translated_subtitles = users_context[user_ip]['translated_subtitles']
|
348 |
+
|
349 |
+
# Saving Translated Subtitles
|
350 |
+
subtitles_path = save_translated_subtitles(translated_subtitles, media_path)
|
351 |
+
|
352 |
+
# Burning Subtitles & Rendering Video
|
353 |
+
rendered_video_path = burn_subtitles(media_path, subtitles_path)
|
354 |
+
|
355 |
+
# Getting Status
|
356 |
+
status = 1 if rendered_video_path else 0
|
357 |
+
|
358 |
+
return {'status': status, "rendered_video_path": rendered_video_path}
|
359 |
+
|
360 |
|
361 |
|
362 |
if __name__ == "__main__":
|
media_download.py
CHANGED
@@ -71,8 +71,26 @@ class YoutubeDownloader(MediaDownloader):
|
|
71 |
self.thumbnail_url = self.youtube.thumbnail_url
|
72 |
self.streams = self.youtube.streams
|
73 |
self.streams_df, self.media_formats_dict = self._get_supported_media_formats()
|
74 |
-
|
75 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
'''
|
77 |
Returns the Quality in Integer
|
78 |
E.g: Given input 1080p, it returns 1080
|
@@ -87,43 +105,97 @@ class YoutubeDownloader(MediaDownloader):
|
|
87 |
'''
|
88 |
Returns all supported media formats for both audio & video
|
89 |
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
|
91 |
-
|
92 |
-
streams_details = []
|
93 |
-
for stream in self.streams.filter(only_video=True):
|
94 |
-
media_type = stream.type
|
95 |
-
media_format = stream.mime_type.split('/')[1]
|
96 |
-
quality = stream.resolution
|
97 |
-
progressive = stream.is_progressive
|
98 |
-
stream_details = [media_type, media_format, quality, progressive]
|
99 |
-
streams_details.append(stream_details)
|
100 |
-
cols = ['media_type', 'media_format', 'media_quality', 'progressive']
|
101 |
-
streams_df = pd.DataFrame(streams_details, columns=cols)
|
102 |
-
|
103 |
-
# Adding Custom Audio Streams
|
104 |
-
streams_df.loc[len(streams_df)] = ['audio', 'mp3', '128kbps', False]
|
105 |
-
streams_df.loc[len(streams_df)] = ['audio', 'mp3', '256kbps', False]
|
106 |
-
streams_df.loc[len(streams_df)] = ['audio', 'wav', '1411kbps', False]
|
107 |
-
|
108 |
-
# Converting to Dictionary for Unique User Options
|
109 |
-
media_formats_dict = dict()
|
110 |
-
for media_type in sorted(streams_df['media_type'].unique()):
|
111 |
-
media_formats_dict[media_type] = dict()
|
112 |
-
media_type_df = streams_df[streams_df['media_type'] == media_type]
|
113 |
-
for media_format in sorted(media_type_df['media_format'].unique()):
|
114 |
-
media_format_df = media_type_df[media_type_df['media_format'] == media_format]
|
115 |
-
media_qualities = sorted(media_format_df['media_quality'].unique(), key=self.__get_quality_int)
|
116 |
-
media_formats_dict[media_type][media_format] = media_qualities
|
117 |
-
|
118 |
-
return streams_df, media_formats_dict
|
119 |
-
|
120 |
-
def get_media_formats(self):
|
121 |
-
'''
|
122 |
-
Returns a dictioary for supported media formats
|
123 |
-
'''
|
124 |
-
return self.media_formats_dict
|
125 |
-
|
126 |
-
def _select_media_format(self):
|
127 |
'''
|
128 |
For selecting media format to download
|
129 |
'''
|
@@ -186,71 +258,164 @@ class YoutubeDownloader(MediaDownloader):
|
|
186 |
|
187 |
return output_path
|
188 |
|
189 |
-
|
190 |
def _download_audio(self, audio_format, audio_quality):
|
191 |
'''
|
192 |
Filters the required audio stream & downloads it
|
193 |
'''
|
|
|
|
|
|
|
|
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
# Getting Output Path
|
199 |
-
output_path = os.path.join(self.output_path, f"{self.title}.{audio_format}")
|
200 |
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
|
210 |
-
|
211 |
-
|
212 |
|
213 |
-
|
|
|
|
|
|
|
214 |
|
215 |
def _download_video(self, video_format, video_quality):
|
216 |
'''
|
217 |
Filters the required video stream & downloads it
|
218 |
Only for Progressive media i.e containing both audio & video streams
|
219 |
'''
|
220 |
-
stream = self.streams.filter(progressive=True, file_extension=video_format, resolution=video_quality).first()
|
221 |
-
print(stream)
|
222 |
-
video_path = stream.download(output_path=self.output_path, filename=f"{self.title}.{video_format}")
|
223 |
-
return video_path
|
224 |
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
-
|
232 |
-
|
233 |
-
print(stream)
|
234 |
-
audio_filename = f"{self.title} - Audio.{media_format}"
|
235 |
-
audio_path = stream.download(output_path=self.output_path, filename=audio_filename)
|
236 |
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
video_path = stream.download(output_path=self.output_path, filename=video_filename)
|
242 |
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
'-c:v', 'copy', '-c:a', 'copy', output_path,
|
247 |
-
'-loglevel', 'quiet']
|
248 |
-
subprocess.run(command)
|
249 |
|
250 |
-
|
251 |
-
|
|
|
252 |
|
253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
|
255 |
def _download_media_chunk(self, media_type, media_format, media_quality):
|
256 |
'''
|
@@ -270,153 +435,95 @@ class YoutubeDownloader(MediaDownloader):
|
|
270 |
'''
|
271 |
Filters the required audio stream & downloads it for particular chunk
|
272 |
'''
|
|
|
|
|
|
|
|
|
|
|
273 |
|
274 |
-
|
275 |
-
|
276 |
-
chunk_string = f"-ss {self.start_time} -to {self.end_time}"
|
277 |
-
|
278 |
-
elif (self.start_time) and (not self.end_time):
|
279 |
-
chunk_string = f"-ss {self.start_time}"
|
280 |
|
281 |
-
|
282 |
-
|
283 |
|
284 |
-
|
285 |
-
|
286 |
|
287 |
-
|
288 |
-
|
289 |
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
|
301 |
-
|
302 |
-
|
303 |
|
304 |
-
|
|
|
|
|
|
|
305 |
|
306 |
def _download_video_chunk(self, video_format, video_quality):
|
307 |
'''
|
308 |
Filters the required video stream & downloads it for particular chunk
|
309 |
'''
|
310 |
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
elif (self.start_time) and (not self.end_time):
|
316 |
-
chunk_string = f"-ss {self.start_time}"
|
317 |
-
|
318 |
-
elif (not self.start_time) and (self.end_time):
|
319 |
-
chunk_string = f"-to {self.end_time}"
|
320 |
-
|
321 |
-
# Getting Output Path
|
322 |
-
output_path = os.path.join(self.output_path, f"{self.title}.{video_format}")
|
323 |
-
|
324 |
-
# Getting Video Quality Integer
|
325 |
-
video_quality = self.__get_quality_int(video_quality)
|
326 |
-
|
327 |
-
# Download Command
|
328 |
-
if video_format == 'mp4':
|
329 |
-
video_codec = "h264"
|
330 |
-
audio_codec = "m4a"
|
331 |
-
|
332 |
-
elif video_format == 'webm':
|
333 |
-
video_codec = "vp9"
|
334 |
-
audio_codec = "opus"
|
335 |
-
|
336 |
-
else:
|
337 |
-
print('Unexpected Video Format Encountered:', video_format)
|
338 |
-
os.exit(0)
|
339 |
-
|
340 |
-
command = [
|
341 |
-
"yt-dlp",
|
342 |
-
url,
|
343 |
-
"-S", f"res:{video_quality},vcodec:{video_codec},acodec:{audio_codec}",
|
344 |
-
"--merge-output-format", video_format,
|
345 |
-
"--download-sections", f"*{self.start_time}-{self.end_time}",
|
346 |
-
"-o", f"{output_path}",
|
347 |
-
# "-q"
|
348 |
-
]
|
349 |
-
|
350 |
-
print(' '.join(command))
|
351 |
|
352 |
-
|
353 |
-
|
354 |
|
355 |
-
|
|
|
356 |
|
357 |
-
|
358 |
-
|
359 |
-
'title': self.title,
|
360 |
-
'media_length': self.media_length,
|
361 |
-
'thumbnail_url': self.thumbnail_url,
|
362 |
-
'formats': self.media_formats_dict
|
363 |
-
}
|
364 |
-
return media_info
|
365 |
-
|
366 |
-
@staticmethod
|
367 |
-
def extract_audio(video_path):
|
368 |
-
"""
|
369 |
-
Extract audio from a video file (MP4 or WebM) and save it as an MP3 file using ffmpeg.
|
370 |
|
371 |
-
|
372 |
-
|
373 |
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
# Path for Extracted Audio File
|
379 |
-
filename, extension = os.path.splitext(video_path)
|
380 |
-
audio_path = filename + '.mp3'
|
381 |
|
382 |
-
|
383 |
-
|
|
|
384 |
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
391 |
|
392 |
-
except
|
393 |
-
print(f"Error: {e}")
|
394 |
-
|
395 |
-
@staticmethod
|
396 |
-
def burn_subtitles(video_file_path, subtitle_file_path):
|
397 |
-
'''
|
398 |
-
Burns the subtitles onto the video
|
399 |
-
|
400 |
-
Args:
|
401 |
-
video_file_path (str): Path to the input video file.
|
402 |
-
subtitle_file_path (str): Path to the subtitle file.
|
403 |
-
|
404 |
-
Returns:
|
405 |
-
str: Path of output video with subtitles.
|
406 |
-
'''
|
407 |
-
try:
|
408 |
-
# Getting Output File Path
|
409 |
-
video_filename, video_extension = os.path.splitext(video_file_path)
|
410 |
-
subtitle_filename, subtitle_extension = os.path.splitext(subtitle_file_path)
|
411 |
-
output_file_path = video_filename + subtitle_extension.replace('.', '_') + video_extension
|
412 |
-
|
413 |
-
# Burning the Subtitles onto Video using FFMPEG Command
|
414 |
-
command = ['ffmpeg', '-i', video_file_path,
|
415 |
-
'-vf', f'subtitles={subtitle_file_path}',
|
416 |
-
output_file_path, '-loglevel', 'quiet']
|
417 |
-
subprocess.run(command, check=True)
|
418 |
-
|
419 |
-
return output_file_path
|
420 |
-
|
421 |
-
except subprocess.CalledProcessError as e:
|
422 |
-
print(f"Error: {e}")
|
|
|
71 |
self.thumbnail_url = self.youtube.thumbnail_url
|
72 |
self.streams = self.youtube.streams
|
73 |
self.streams_df, self.media_formats_dict = self._get_supported_media_formats()
|
74 |
+
|
75 |
+
def get_media_formats(self):
|
76 |
+
'''
|
77 |
+
Returns a dictionary for supported media formats
|
78 |
+
'''
|
79 |
+
return self.media_formats_dict
|
80 |
+
|
81 |
+
def get_media_metadata(self):
|
82 |
+
'''
|
83 |
+
Returns a dictionary for media metadata
|
84 |
+
'''
|
85 |
+
media_info = {
|
86 |
+
'title': self.title,
|
87 |
+
'media_length': self.media_length,
|
88 |
+
'thumbnail_url': self.thumbnail_url
|
89 |
+
}
|
90 |
+
return media_info
|
91 |
+
|
92 |
+
@staticmethod
|
93 |
+
def __get_quality_int(media_quality):
|
94 |
'''
|
95 |
Returns the Quality in Integer
|
96 |
E.g: Given input 1080p, it returns 1080
|
|
|
105 |
'''
|
106 |
Returns all supported media formats for both audio & video
|
107 |
'''
|
108 |
+
|
109 |
+
try:
|
110 |
+
# Creating Pandas Dataframe for Video Streams' Details
|
111 |
+
streams_details = []
|
112 |
+
for stream in self.streams.filter(only_video=True):
|
113 |
+
media_type = stream.type
|
114 |
+
media_format = stream.mime_type.split('/')[1]
|
115 |
+
quality = stream.resolution
|
116 |
+
progressive = stream.is_progressive
|
117 |
+
stream_details = [media_type, media_format, quality, progressive]
|
118 |
+
streams_details.append(stream_details)
|
119 |
+
cols = ['media_type', 'media_format', 'media_quality', 'progressive']
|
120 |
+
streams_df = pd.DataFrame(streams_details, columns=cols)
|
121 |
+
|
122 |
+
# Adding Custom Audio Streams
|
123 |
+
streams_df.loc[len(streams_df)] = ['audio', 'mp3', '128kbps', False]
|
124 |
+
streams_df.loc[len(streams_df)] = ['audio', 'mp3', '256kbps', False]
|
125 |
+
streams_df.loc[len(streams_df)] = ['audio', 'wav', '1411kbps', False]
|
126 |
+
|
127 |
+
# Converting to Dictionary for Unique User Options
|
128 |
+
media_formats_dict = dict()
|
129 |
+
for media_type in sorted(streams_df['media_type'].unique()):
|
130 |
+
media_formats_dict[media_type] = dict()
|
131 |
+
media_type_df = streams_df[streams_df['media_type'] == media_type]
|
132 |
+
for media_format in sorted(media_type_df['media_format'].unique()):
|
133 |
+
media_format_df = media_type_df[media_type_df['media_format'] == media_format]
|
134 |
+
media_qualities = sorted(media_format_df['media_quality'].unique(), key=self.__get_quality_int)
|
135 |
+
media_formats_dict[media_type][media_format] = media_qualities
|
136 |
+
|
137 |
+
return streams_df, media_formats_dict
|
138 |
+
|
139 |
+
except Exception as pytube_error:
|
140 |
+
print(f"PyTube Error in _get_supported_media_formats: \n{pytube_error}\n")
|
141 |
+
print('Trying with yt-dlp...')
|
142 |
+
|
143 |
+
try:
|
144 |
+
# Download Command
|
145 |
+
command = ["yt-dlp", "--list-formats", url,
|
146 |
+
"--get-filename", "--format", "best[ext=mp4]/best[ext=webm]"]
|
147 |
+
print(' '.join(command))
|
148 |
+
|
149 |
+
# Running the command using Subprocess and capturing the output
|
150 |
+
completed_process = subprocess.run(command, text=True, stdout=subprocess.PIPE)
|
151 |
+
|
152 |
+
if completed_process.returncode != 0:
|
153 |
+
print(f"yt-dlp error in _get_supported_media_formats:")
|
154 |
+
print(completed_process.stderr)
|
155 |
+
else:
|
156 |
+
output_lines = completed_process.stdout.split('\n')
|
157 |
+
output_lines = [line for line in output_lines if line.strip()]
|
158 |
+
|
159 |
+
# Create a list of dictionaries for each format entry
|
160 |
+
streams_details = []
|
161 |
+
for line in output_lines[2:]: # Skip the header lines
|
162 |
+
fields = line.split()
|
163 |
+
media_format = fields[1]
|
164 |
+
media_quality = fields[-2]
|
165 |
+
if media_format in ['mp4', 'webm']:
|
166 |
+
if 'p,' in media_quality:
|
167 |
+
media_type = 'video'
|
168 |
+
media_quality = media_quality[:-1]
|
169 |
+
progressive = False
|
170 |
+
stream_details = [media_type, media_format, media_quality, progressive]
|
171 |
+
streams_details.append(stream_details)
|
172 |
+
|
173 |
+
# Create a pandas DataFrame from the list of dictionaries
|
174 |
+
cols = ['media_type', 'media_format', 'media_quality', 'progressive']
|
175 |
+
streams_df = pd.DataFrame(streams_details, columns=cols)
|
176 |
+
streams_df = streams_df.drop_duplicates().reset_index(drop=True)
|
177 |
+
|
178 |
+
# Adding Custom Audio Streams
|
179 |
+
streams_df.loc[len(streams_df)] = ['audio', 'mp3', '128kbps', False]
|
180 |
+
streams_df.loc[len(streams_df)] = ['audio', 'mp3', '256kbps', False]
|
181 |
+
streams_df.loc[len(streams_df)] = ['audio', 'wav', '1411kbps', False]
|
182 |
+
|
183 |
+
# Converting to Dictionary for Unique User Options
|
184 |
+
media_formats_dict = dict()
|
185 |
+
for media_type in sorted(streams_df['media_type'].unique()):
|
186 |
+
media_formats_dict[media_type] = dict()
|
187 |
+
media_type_df = streams_df[streams_df['media_type'] == media_type]
|
188 |
+
for media_format in sorted(media_type_df['media_format'].unique()):
|
189 |
+
media_format_df = media_type_df[media_type_df['media_format'] == media_format]
|
190 |
+
media_qualities = sorted(media_format_df['media_quality'].unique(), key=self.__get_quality_int)
|
191 |
+
media_formats_dict[media_type][media_format] = media_qualities
|
192 |
+
|
193 |
+
return streams_df, media_formats_dict
|
194 |
+
|
195 |
+
except Exception as yt_dlp_error:
|
196 |
+
print(f"yt-dlp error in _get_supported_media_formats: \n{yt_dlp_error}\n")
|
197 |
|
198 |
+
def select_media_format(self):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
'''
|
200 |
For selecting media format to download
|
201 |
'''
|
|
|
258 |
|
259 |
return output_path
|
260 |
|
|
|
261 |
def _download_audio(self, audio_format, audio_quality):
|
262 |
'''
|
263 |
Filters the required audio stream & downloads it
|
264 |
'''
|
265 |
+
|
266 |
+
try:
|
267 |
+
# Getting Quality Command String
|
268 |
+
quality = str(self.__get_quality_int(audio_quality)) + 'K'
|
269 |
|
270 |
+
# Getting Output Path
|
271 |
+
output_path = os.path.join(self.output_path, f"{self.title}.{audio_format}")
|
|
|
|
|
|
|
272 |
|
273 |
+
# Download Command
|
274 |
+
command = [
|
275 |
+
"yt-dlp",
|
276 |
+
"-x", "--audio-format", audio_format,
|
277 |
+
"--audio-quality", quality,
|
278 |
+
"-o", output_path,
|
279 |
+
self.url, "-q"
|
280 |
+
]
|
281 |
|
282 |
+
# Running the command using Subprocess
|
283 |
+
subprocess.run(command)
|
284 |
|
285 |
+
return output_path
|
286 |
+
|
287 |
+
except Exception as yt_dlp_error:
|
288 |
+
print(f"Error in _download_audio: \n{yt_dlp_error}\n")
|
289 |
|
290 |
def _download_video(self, video_format, video_quality):
|
291 |
'''
|
292 |
Filters the required video stream & downloads it
|
293 |
Only for Progressive media i.e containing both audio & video streams
|
294 |
'''
|
|
|
|
|
|
|
|
|
295 |
|
296 |
+
try:
|
297 |
+
stream = self.streams.filter(progressive=True, file_extension=video_format, resolution=video_quality).first()
|
298 |
+
print(stream)
|
299 |
+
video_path = stream.download(output_path=self.output_path, filename=f"{self.title}.{video_format}")
|
300 |
+
return video_path
|
301 |
+
|
302 |
+
except Exception as pytube_error:
|
303 |
+
print(f"PyTube error in _download_video: \n{pytube_error}\n")
|
304 |
+
print('Trying with yt-dlp...')
|
305 |
+
|
306 |
+
try:
|
307 |
+
# Getting Output Path
|
308 |
+
output_path = os.path.join(self.output_path, f"{self.title}.{video_format}")
|
309 |
|
310 |
+
# Getting Video Quality Integer
|
311 |
+
video_quality = self.__get_quality_int(video_quality)
|
|
|
|
|
|
|
312 |
|
313 |
+
# Setting Formats
|
314 |
+
if video_format == 'mp4':
|
315 |
+
video_codec = "h264"
|
316 |
+
audio_codec = "m4a"
|
|
|
317 |
|
318 |
+
elif video_format == 'webm':
|
319 |
+
video_codec = "vp9"
|
320 |
+
audio_codec = "opus"
|
|
|
|
|
|
|
321 |
|
322 |
+
else:
|
323 |
+
print('Unexpected Video Format Encountered:', video_format)
|
324 |
+
os.exit(0)
|
325 |
|
326 |
+
# Download Command
|
327 |
+
command = [
|
328 |
+
"yt-dlp",
|
329 |
+
url,
|
330 |
+
"-S", f"res:{video_quality},vcodec:{video_codec},acodec:{audio_codec}",
|
331 |
+
"--merge-output-format", video_format,
|
332 |
+
"-o", f"{output_path}",
|
333 |
+
"-q"
|
334 |
+
]
|
335 |
+
print(' '.join(command))
|
336 |
+
|
337 |
+
# Running the command using Subprocess
|
338 |
+
subprocess.run(command, check=True)
|
339 |
+
|
340 |
+
return output_path
|
341 |
+
|
342 |
+
except Exception as yt_dlp_error:
|
343 |
+
print(f"yt-dlp error in _download_video: \n{yt_dlp_error}\n")
|
344 |
+
|
345 |
+
def _download_audio_and_video(self, media_format, media_quality):
|
346 |
+
'''
|
347 |
+
Filters the required video stream & downloads it
|
348 |
+
Filters the best quality audio stream of the same format & downloads it
|
349 |
+
'''
|
350 |
+
|
351 |
+
try:
|
352 |
+
# Downloading Audio
|
353 |
+
stream = self.streams.filter(file_extension=media_format, only_audio=True).order_by('abr').desc().first()
|
354 |
+
print(stream)
|
355 |
+
audio_filename = f"{self.title} - Audio.{media_format}"
|
356 |
+
audio_path = stream.download(output_path=self.output_path, filename=audio_filename)
|
357 |
+
|
358 |
+
# Downloading Video
|
359 |
+
stream = self.streams.filter(file_extension=media_format, resolution=media_quality).first()
|
360 |
+
print(stream)
|
361 |
+
video_filename = f"{self.title} - Video.{media_format}"
|
362 |
+
video_path = stream.download(output_path=self.output_path, filename=video_filename)
|
363 |
+
|
364 |
+
# Combining the Audio & Video Files using FFMPEG Command
|
365 |
+
output_path = os.path.join(self.output_path, f"{self.title}.{media_format}")
|
366 |
+
command = ['ffmpeg', '-i', video_path, '-i', audio_path,
|
367 |
+
'-c:v', 'copy', '-c:a', 'copy', output_path,
|
368 |
+
'-loglevel', 'quiet']
|
369 |
+
subprocess.run(command)
|
370 |
+
|
371 |
+
os.remove(audio_path)
|
372 |
+
os.remove(video_path)
|
373 |
+
|
374 |
+
return output_path
|
375 |
+
|
376 |
+
except Exception as pytube_error:
|
377 |
+
print(f"PyTube error in _download_audio_and_video: \n{pytube_error}\n")
|
378 |
+
print('Trying with yt-dlp...')
|
379 |
+
|
380 |
+
try:
|
381 |
+
|
382 |
+
# Getting Output Path
|
383 |
+
output_path = os.path.join(self.output_path, f"{self.title}.{media_format}")
|
384 |
+
|
385 |
+
# Getting Video Quality Integer
|
386 |
+
media_quality = self.__get_quality_int(media_quality)
|
387 |
+
|
388 |
+
# Setting Formats
|
389 |
+
if media_format == 'mp4':
|
390 |
+
video_codec = "h264"
|
391 |
+
audio_codec = "m4a"
|
392 |
+
|
393 |
+
elif media_format == 'webm':
|
394 |
+
video_codec = "vp9"
|
395 |
+
audio_codec = "opus"
|
396 |
+
|
397 |
+
else:
|
398 |
+
print('Unexpected Video Format Encountered:', media_format)
|
399 |
+
os.exit(0)
|
400 |
+
|
401 |
+
# Download Command
|
402 |
+
command = [
|
403 |
+
"yt-dlp",
|
404 |
+
url,
|
405 |
+
"-S", f"res:{media_quality},vcodec:{video_codec},acodec:{audio_codec}",
|
406 |
+
"--merge-output-format", media_format,
|
407 |
+
"-o", f"{output_path}",
|
408 |
+
"-q"
|
409 |
+
]
|
410 |
+
print(' '.join(command))
|
411 |
+
|
412 |
+
# Running the command using Subprocess
|
413 |
+
subprocess.run(command)
|
414 |
+
|
415 |
+
return output_path
|
416 |
+
|
417 |
+
except Exception as yt_dlp_error:
|
418 |
+
print(f"yt-dlp error in _download_audio_and_video: \n{yt_dlp_error}\n")
|
419 |
|
420 |
def _download_media_chunk(self, media_type, media_format, media_quality):
|
421 |
'''
|
|
|
435 |
'''
|
436 |
Filters the required audio stream & downloads it for particular chunk
|
437 |
'''
|
438 |
+
|
439 |
+
try:
|
440 |
+
# Getting Chunk Command String
|
441 |
+
if (self.start_time) and (self.end_time):
|
442 |
+
chunk_string = f"-ss {self.start_time} -to {self.end_time}"
|
443 |
|
444 |
+
elif (self.start_time) and (not self.end_time):
|
445 |
+
chunk_string = f"-ss {self.start_time}"
|
|
|
|
|
|
|
|
|
446 |
|
447 |
+
elif (not self.start_time) and (self.end_time):
|
448 |
+
chunk_string = f"-to {self.end_time}"
|
449 |
|
450 |
+
# Getting Quality Command String
|
451 |
+
quality = str(self.__get_quality_int(audio_quality)) + 'K'
|
452 |
|
453 |
+
# Getting Output Path
|
454 |
+
output_path = os.path.join(self.output_path, f"{self.title}.{audio_format}")
|
455 |
|
456 |
+
# Download Command
|
457 |
+
command = [
|
458 |
+
"yt-dlp",
|
459 |
+
"-x", "--audio-format", audio_format,
|
460 |
+
"--audio-quality", quality,
|
461 |
+
"--external-downloader", "ffmpeg",
|
462 |
+
"--external-downloader-args", chunk_string,
|
463 |
+
"-o", output_path,
|
464 |
+
url, "-q"
|
465 |
+
]
|
466 |
|
467 |
+
# Running the command using Subprocess
|
468 |
+
subprocess.run(command)
|
469 |
|
470 |
+
return output_path
|
471 |
+
|
472 |
+
except Exception as e:
|
473 |
+
print(f"Error in _download_audio_chunk: {e}")
|
474 |
|
475 |
def _download_video_chunk(self, video_format, video_quality):
|
476 |
'''
|
477 |
Filters the required video stream & downloads it for particular chunk
|
478 |
'''
|
479 |
|
480 |
+
try:
|
481 |
+
# Getting Chunk Command String
|
482 |
+
if (self.start_time) and (self.end_time):
|
483 |
+
chunk_string = f"-ss {self.start_time} -to {self.end_time}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
484 |
|
485 |
+
elif (self.start_time) and (not self.end_time):
|
486 |
+
chunk_string = f"-ss {self.start_time}"
|
487 |
|
488 |
+
elif (not self.start_time) and (self.end_time):
|
489 |
+
chunk_string = f"-to {self.end_time}"
|
490 |
|
491 |
+
# Getting Output Path
|
492 |
+
output_path = os.path.join(self.output_path, f"{self.title}.{video_format}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
493 |
|
494 |
+
# Getting Video Quality Integer
|
495 |
+
video_quality = self.__get_quality_int(video_quality)
|
496 |
|
497 |
+
# Setting Formats
|
498 |
+
if video_format == 'mp4':
|
499 |
+
video_codec = "h264"
|
500 |
+
audio_codec = "m4a"
|
|
|
|
|
|
|
501 |
|
502 |
+
elif video_format == 'webm':
|
503 |
+
video_codec = "vp9"
|
504 |
+
audio_codec = "opus"
|
505 |
|
506 |
+
else:
|
507 |
+
print('Unexpected Video Format Encountered:', video_format)
|
508 |
+
os.exit(0)
|
509 |
+
|
510 |
+
# Download Command
|
511 |
+
command = [
|
512 |
+
"yt-dlp",
|
513 |
+
url,
|
514 |
+
"-S", f"res:{video_quality},vcodec:{video_codec},acodec:{audio_codec}",
|
515 |
+
"--merge-output-format", video_format,
|
516 |
+
"--download-sections", f"*{self.start_time}-{self.end_time}",
|
517 |
+
"-o", f"{output_path}",
|
518 |
+
"-q"
|
519 |
+
]
|
520 |
+
|
521 |
+
print(' '.join(command))
|
522 |
+
|
523 |
+
# Running the command using Subprocess
|
524 |
+
subprocess.run(command)
|
525 |
+
|
526 |
+
return output_path
|
527 |
|
528 |
+
except Exception as e:
|
529 |
+
print(f"Error in _download_video_chunk: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
summarizer.py
CHANGED
@@ -415,13 +415,13 @@ class AudioBookNarration:
|
|
415 |
def get_stuff_prompt(self):
|
416 |
|
417 |
prompt_template = """
|
418 |
-
Create a {
|
419 |
So provide the output that is verbose, easier to understand and full of expressions.
|
420 |
{text}
|
421 |
|
422 |
"""
|
423 |
prompt = PromptTemplate(
|
424 |
-
template=prompt_template, input_variables=['
|
425 |
|
426 |
|
427 |
return prompt
|
@@ -429,29 +429,29 @@ class AudioBookNarration:
|
|
429 |
def define_prompts(self):
|
430 |
|
431 |
map_prompts = """
|
432 |
-
Create a {
|
433 |
So provide the output that is verbose, easier to understand and full of expressions.
|
434 |
{text}
|
435 |
"""
|
436 |
|
437 |
combine_prompt = """
|
438 |
Below are the list of text that represent narration from the text.
|
439 |
-
Your job is to combine these narrations and craete one verbose,easier to understand and full of experssions {
|
440 |
{text}
|
441 |
|
442 |
"""
|
443 |
|
444 |
|
445 |
|
446 |
-
map_template = PromptTemplate(template=map_prompts, input_variables=['
|
447 |
)
|
448 |
combine_template = PromptTemplate(
|
449 |
-
template=combine_prompt, input_variables=['
|
450 |
|
451 |
return map_template, combine_template
|
452 |
# pass
|
453 |
|
454 |
-
def define_chain(self,
|
455 |
|
456 |
|
457 |
docs, chain_type = self.load_docs()
|
@@ -470,7 +470,7 @@ class AudioBookNarration:
|
|
470 |
llm=llm, map_prompt=map_prompts, combine_prompt=combine_prompt, chain_type='map_reduce', verbose=False)
|
471 |
|
472 |
|
473 |
-
output = chain.run(
|
474 |
|
475 |
# self.create_wordcloud(output=output)
|
476 |
# display(Markdown(f"Text: {docs}"))
|
|
|
415 |
def get_stuff_prompt(self):
|
416 |
|
417 |
prompt_template = """
|
418 |
+
Create a {narration_style} narration for this below text. This narration will be used for audiobook generation.
|
419 |
So provide the output that is verbose, easier to understand and full of expressions.
|
420 |
{text}
|
421 |
|
422 |
"""
|
423 |
prompt = PromptTemplate(
|
424 |
+
template=prompt_template, input_variables=['narration_style','text'])
|
425 |
|
426 |
|
427 |
return prompt
|
|
|
429 |
def define_prompts(self):
|
430 |
|
431 |
map_prompts = """
|
432 |
+
Create a {narration_style} narration for this below text. This narration will be used for audiobook generation.
|
433 |
So provide the output that is verbose, easier to understand and full of expressions.
|
434 |
{text}
|
435 |
"""
|
436 |
|
437 |
combine_prompt = """
|
438 |
Below are the list of text that represent narration from the text.
|
439 |
+
Your job is to combine these narrations and craete one verbose,easier to understand and full of experssions {narration_style} narration.
|
440 |
{text}
|
441 |
|
442 |
"""
|
443 |
|
444 |
|
445 |
|
446 |
+
map_template = PromptTemplate(template=map_prompts, input_variables=['narration_style','text']
|
447 |
)
|
448 |
combine_template = PromptTemplate(
|
449 |
+
template=combine_prompt, input_variables=['narration_style','text'])
|
450 |
|
451 |
return map_template, combine_template
|
452 |
# pass
|
453 |
|
454 |
+
def define_chain(self,narration_style=None,chain_type=None):
|
455 |
|
456 |
|
457 |
docs, chain_type = self.load_docs()
|
|
|
470 |
llm=llm, map_prompt=map_prompts, combine_prompt=combine_prompt, chain_type='map_reduce', verbose=False)
|
471 |
|
472 |
|
473 |
+
output = chain.run(narration_style = narration_style,input_documents = docs)
|
474 |
|
475 |
# self.create_wordcloud(output=output)
|
476 |
# display(Markdown(f"Text: {docs}"))
|
temp/translated_subtitles.json
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"start":0.0,
|
4 |
+
"end":6.0,
|
5 |
+
"text":"دوسرا پہلو ٹیلی ویژن اور فلمیں اور موسیقی ہے۔"
|
6 |
+
},
|
7 |
+
{
|
8 |
+
"start":6.0,
|
9 |
+
"end":12.0,
|
10 |
+
"text":"اسلام کے بارے میں دلچسپ باتوں میں سے ایک یہ ہے کہ علمائے کرام کا موسیقی کے ساتھ تناؤ تھا۔"
|
11 |
+
},
|
12 |
+
{
|
13 |
+
"start":12.0,
|
14 |
+
"end":17.0,
|
15 |
+
"text":"کوئی بھی یہ واضح بیان نہیں دے سکتا کہ یہ مجمل ہے۔"
|
16 |
+
},
|
17 |
+
{
|
18 |
+
"start":17.0,
|
19 |
+
"end":26.0,
|
20 |
+
"text":"اگر آپ شوکانی اور نیل الاوطار پڑھیں تو اس کے پاس اس پر ایک سیکشن ہے اور اس میں موسیقی کے بارے میں موجود خلافت کے بارے میں بات کرتا ہے۔"
|
21 |
+
},
|
22 |
+
{
|
23 |
+
"start":30.0,
|
24 |
+
"end":38.18,
|
25 |
+
"text":"سماع کے بارے میں علماء کے حصے ہیں جو کہ موسیقی کی ایک مقدس قسم ہے جسے وہ اچھی چیز اور جائز سمجھتے تھے۔"
|
26 |
+
},
|
27 |
+
{
|
28 |
+
"start":39.0,
|
29 |
+
"end":46.0,
|
30 |
+
"text":"لیکن علمائے کرام موسیقی سے بہت محتاط تھے کیونکہ وہ سمجھتے تھے کہ موسیقی کتنی طاقتور ہے،"
|
31 |
+
},
|
32 |
+
{
|
33 |
+
"start":46.0,
|
34 |
+
"end":50.0,
|
35 |
+
"text":"اور یہ کتنا پرکشش ہے، اور اس کے روح پر کیا اثرات مرتب ہوتے ہیں۔"
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"start":50.0,
|
39 |
+
"end":53.0,
|
40 |
+
"text":"یہ اصل میں قدیم یونانیوں کو واپس جاتا ہے."
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"start":53.0,
|
44 |
+
"end":58.0,
|
45 |
+
"text":"جمہوریہ میں افلاطون، سقراط وہ آواز ہے جسے وہ استعمال کرتا ہے۔"
|
46 |
+
},
|
47 |
+
{
|
48 |
+
"start":60.0,
|
49 |
+
"end":67.0,
|
50 |
+
"text":"وہ دراصل جمہوریہ میں موسیقی کی کچھ اقسام کو غیر قانونی قرار دیتا ہے کیونکہ وہ روح کے لیے بہت نقصان دہ تھے۔"
|
51 |
+
},
|
52 |
+
{
|
53 |
+
"start":67.0,
|
54 |
+
"end":71.0,
|
55 |
+
"text":"ایتھوس تھیوری موسیقی کے اثر کا نظریہ ہے۔"
|
56 |
+
},
|
57 |
+
{
|
58 |
+
"start":71.0,
|
59 |
+
"end":79.0,
|
60 |
+
"text":"درحقیقت الفارابی جس نے موسیقی کی سب سے بڑی کتاب کتاب الموسیق الکبیر لکھی۔"
|
61 |
+
},
|
62 |
+
{
|
63 |
+
"start":79.0,
|
64 |
+
"end":83.0,
|
65 |
+
"text":"جو میری لائبریری میں ہے، یہ ایک بہت بڑی کتاب ہے، بہت بھاری۔"
|
66 |
+
},
|
67 |
+
{
|
68 |
+
"start":83.0,
|
69 |
+
"end":89.0,
|
70 |
+
"text":"یہ موسیقی کی سائنس پر ابتدائی سنجیدہ کاموں میں سے ایک ہے۔"
|
71 |
+
},
|
72 |
+
{
|
73 |
+
"start":90.0,
|
74 |
+
"end":103.0,
|
75 |
+
"text":"وہ لوگوں کو ہنسانے، رونے، نیند آنے، یا مقام کی بنیاد پر پرجوش ہونے کے قابل ہونے کے لیے جانا جاتا تھا جو وہ عود پر بجاتا تھا۔"
|
76 |
+
},
|
77 |
+
{
|
78 |
+
"start":104.04,
|
79 |
+
"end":106.0,
|
80 |
+
"text":"وہ لفظی طور پر لوگوں کو رلا سکتا تھا۔"
|
81 |
+
},
|
82 |
+
{
|
83 |
+
"start":106.0,
|
84 |
+
"end":107.36,
|
85 |
+
"text":"اور یہ اچھی طرح سے درج ہے۔"
|
86 |
+
},
|
87 |
+
{
|
88 |
+
"start":108.66,
|
89 |
+
"end":112.0,
|
90 |
+
"text":"اور جب لوگ کنسرٹس میں جاتے ہیں تو آپ کو یہی ملتا ہے۔"
|
91 |
+
},
|
92 |
+
{
|
93 |
+
"start":112.0,
|
94 |
+
"end":115.0,
|
95 |
+
"text":"وہ بہت مشتعل ہو جاتے ہیں، انہیں حرکت کرنا پڑتی ہے۔"
|
96 |
+
},
|
97 |
+
{
|
98 |
+
"start":115.0,
|
99 |
+
"end":117.0,
|
100 |
+
"text":"وہ نہیں جانتے کیوں، لیکن انہیں منتقل ہونا پڑے گا."
|
101 |
+
},
|
102 |
+
{
|
103 |
+
"start":120.0,
|
104 |
+
"end":127.0,
|
105 |
+
"text":"آوازوں کے شیطانی اثرات ہوتے ہیں، اور پھر فرشتے کے اثرات ہوتے ہیں جو آوازوں کے ہوتے ہیں۔"
|
106 |
+
},
|
107 |
+
{
|
108 |
+
"start":127.0,
|
109 |
+
"end":133.0,
|
110 |
+
"text":"اور یوں علمائے کرام آواز کے شیطانی اثرات کے بارے میں بہت فکر مند تھے۔"
|
111 |
+
},
|
112 |
+
{
|
113 |
+
"start":134.08,
|
114 |
+
"end":141.0,
|
115 |
+
"text":"اور وہ سمجھ گئے کہ جن چیزوں کو شیاطین ہمیشہ استعمال کرتے رہے ہیں وہ موسیقی ہے لوگوں کو اپنی طرف راغب کرنے کے لیے۔"
|
116 |
+
},
|
117 |
+
{
|
118 |
+
"start":141.0,
|
119 |
+
"end":146.34,
|
120 |
+
"text":"درحقیقت داؤد علیہ السلام کے بارے میں حدیث میں ہے کہ جنہوں نے زبور گایا،"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"start":150.0,
|
124 |
+
"end":153.0,
|
125 |
+
"text":"یہ روحانی نہیں تھا، لیکن یہ فرشتہ تھا۔"
|
126 |
+
},
|
127 |
+
{
|
128 |
+
"start":153.46,
|
129 |
+
"end":158.0,
|
130 |
+
"text":"شیطان نے ایک چھوٹا سا بینڈ اکٹھا کیا، اور اس نے اسے سڑک کے کنارے رکھ دیا،"
|
131 |
+
},
|
132 |
+
{
|
133 |
+
"start":158.0,
|
134 |
+
"end":162.78,
|
135 |
+
"text":"اور لوگ داؤد کو سننے کے لیے جاتے تھے اور شیطان کے ٹولے کو سنتے تھے۔"
|
136 |
+
},
|
137 |
+
{
|
138 |
+
"start":163.0,
|
139 |
+
"end":166.0,
|
140 |
+
"text":"اور وہ داؤد کو بھول جائیں گے۔"
|
141 |
+
},
|
142 |
+
{
|
143 |
+
"start":166.0,
|
144 |
+
"end":170.0,
|
145 |
+
"text":"اور یوں یہ وہ تناؤ تھا جو علمائے کرام کو تھا، اور یہ بہت ضروری ہے کہ یہ تناؤ موجود رہے،"
|
146 |
+
},
|
147 |
+
{
|
148 |
+
"start":170.08,
|
149 |
+
"end":175.0,
|
150 |
+
"text":"کیونکہ کوئی بھی یہ بیان نہیں کر سکتا کہ موسیقی مکمل طور پر حرام ہے،"
|
151 |
+
},
|
152 |
+
{
|
153 |
+
"start":175.0,
|
154 |
+
"end":178.0,
|
155 |
+
"text":"اور کوئی بھی یہ نہیں کہہ سکتا کہ یہ حلال ہے۔"
|
156 |
+
},
|
157 |
+
{
|
158 |
+
"start":180.0,
|
159 |
+
"end":185.0,
|
160 |
+
"text":"اور مسلمان اس چیز میں کبھی زیادہ دور نہیں جاتے، کیونکہ اب آپ مغرب میں دیکھتے ہیں،"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"start":185.46,
|
164 |
+
"end":189.0,
|
165 |
+
"text":"لوگ ہر وقت موسیقی سنتے ہیں، ان کے پاس نہیں ہے، وہ ہمیشہ پلگ ان ہوتے ہیں۔"
|
166 |
+
},
|
167 |
+
{
|
168 |
+
"start":189.0,
|
169 |
+
"end":190.84,
|
170 |
+
"text":"وہ اپنی گاڑی میں بیٹھتے ہیں، وہ میوزک آن کرتے ہیں۔"
|
171 |
+
},
|
172 |
+
{
|
173 |
+
"start":191.0,
|
174 |
+
"end":195.0,
|
175 |
+
"text":"وہ چلتے ہیں، وہ اپنے ائرفون لگاتے ہیں، اور وہ اپنی موسیقی سنتے ہیں،"
|
176 |
+
},
|
177 |
+
{
|
178 |
+
"start":195.0,
|
179 |
+
"end":199.0,
|
180 |
+
"text":"اور لوگوں کے پاس لمبی پلے لسٹ ہوتی ہے، وہ ان چیزوں پر بہت پیسہ خرچ کرتے ہیں،"
|
181 |
+
},
|
182 |
+
{
|
183 |
+
"start":199.0,
|
184 |
+
"end":204.0,
|
185 |
+
"text":"اور اس طرح وہ کھو گئے ہیں، ان کے پاس مزید سوچنے کا وقت نہیں ہے،"
|
186 |
+
},
|
187 |
+
{
|
188 |
+
"start":204.0,
|
189 |
+
"end":206.0,
|
190 |
+
"text":"کیونکہ ان کی زندگی آوازوں سے بھری ہوئی ہے۔"
|
191 |
+
},
|
192 |
+
{
|
193 |
+
"start":210.0,
|
194 |
+
"end":215.0,
|
195 |
+
"text":"جو ان کے پاس پہلے کبھی نہیں تھا، اور وہ انہیں بہت مؤثر طریقے سے استعمال کر رہے ہیں۔"
|
196 |
+
},
|
197 |
+
{
|
198 |
+
"start":215.3,
|
199 |
+
"end":221.0,
|
200 |
+
"text":"اور بدقسمتی سے ہم حق کے لوگ ان کا مؤثر استعمال نہیں کر رہے ہیں۔"
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"start":221.0,
|
204 |
+
"end":222.0,
|
205 |
+
"text":"اور اللہ فرماتا ہے"
|
206 |
+
},
|
207 |
+
{
|
208 |
+
"start":226.0,
|
209 |
+
"end":229.0,
|
210 |
+
"text":"ان اوزاروں سے لڑو جن سے آپ لڑ رہے ہیں۔"
|
211 |
+
},
|
212 |
+
{
|
213 |
+
"start":229.0,
|
214 |
+
"end":234.0,
|
215 |
+
"text":"اس دور کا جہاد کانوں کے درمیان ہے۔"
|
216 |
+
},
|
217 |
+
{
|
218 |
+
"start":234.0,
|
219 |
+
"end":238.0,
|
220 |
+
"text":"اس دور کا جہاد کانوں کے درمیان ہے۔"
|
221 |
+
},
|
222 |
+
{
|
223 |
+
"start":240.0,
|
224 |
+
"end":245.0,
|
225 |
+
"text":"میدان جنگ انسانوں کا دماغ ہے اور وسعت کے لحاظ سے دل۔"
|
226 |
+
}
|
227 |
+
]
|
temp/translated_transcript.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
دوسرا پہلو ٹیلی ویژن، فلمیں اور موسیقی ہے. اسلام کے بارے میں ایک دلچسپ بات یہ ہے کہ علماء کی موسیقی کے ساتھ کشیدگی تھی. کوئی بھی یہ بات واضح نہیں کر سکتا کہ یہ مجمل علی ہے. اگر آپ شوکانی اور نائل الاوطار پڑھتے ہیں تو اس کے پاس اس پر ایک سیکشن ہے اور وہ موسیقی کے بارے میں موجود خلافت کے بارے میں بات کرتا ہے. علماء کے پاس سما کے بارے میں سیکشن ہیں، جو ایک مقدس قسم کی موسیقی ہے جو ان کے خیال میں ایک اچھی چیز اور جائز ہے. لیکن علماء موسیقی سے بہت محتاط تھے کیونکہ وہ سمجھتے تھے کہ موسیقی کتنی طاقتور ہے اور کتنی دلکش ہے، اور روح پر اس کے اثرات. یہ اصل میں قدیم یونانیوں سے واپس جاتا ہے. جمہوریہ میں افلاطون، سقراط وہ آواز ہے جو وہ استعمال کرتا ہے. وہ اصل میں جمہوریہ میں موسیقی کی کچھ اقسام کو غیر قانونی قرار دیتا ہے کیونکہ وہ روح کے لئے بہت نقصان دہ تھے. ایتوس تھیوری اس اثر کا نظریہ ہے کہ موسیقی. دراصل، الفارابی، جس نے کتب الموسيق الكبير لکھی، موسیقی کی بڑی کتاب، جو میرے پاس اپنی لائبریری میں ہے، یہ ایک بہت بڑی کتاب ہے، بہت بھاری. یہ موسیقی کے علم پر سب سے پہلے سنجیدہ کام میں سے ایک ہے. وہ لوگوں کو ہنسنے ، رونے ، نیند آنے یا شوق سے کھیلنے کے قابل ہونے کے لئے جانا جاتا تھا. وہ لفظی طور پر لوگوں کو روانا کر سکتا تھا. اور یہ اچھی طرح ریکارڈ کیا گیا ہے. اور یہ آپ کو ملتا ہے جب لوگ کنسرٹ پر جاتے ہیں. وہ بہت پریشان ہو جاتے ہیں، انہیں منتقل کرنا پڑتا ہے. وہ نہیں جانتے کیوں، لیکن وہ منتقل کرنا ہے. آوازوں کے شیطانی اثرات ہوتے ہیں، اور پھر فرشتے بھی ہوتے ہیں جن کے اثرات ہوتے ہیں. اور اس لئے علماء کو آواز کے شیطانی اثرات سے بہت فکر مند تھے. اور وہ سمجھ گئے کہ جنات نے ہمیشہ لوگوں کو دور کرنے کے لئے موسیقی کا استعمال کیا ہے. دراصل، داؤد، سلام ہو اس پر، جو زبور گاتا تھا کے بارے میں حدیث میں، یہ روحانی نہیں تھا، لیکن یہ فرشتہ تھا. شیطان نے ایک چھوٹا سا بینڈ اکٹھا کیا اور اس نے اسے سڑک کے کنارے رکھا اور لوگ جو داؤد کو سننے کے لئے جا رہے تھے وہ رک کر شیطان کے بینڈ کو سنتے تھے اور وہ داؤد کو بھول جاتے تھے. اور تو یہ کشیدگی تھی جو علماء نے کی تھی، اور یہ بہت اہم ہے کہ کشیدگی موجود ہے کیونکہ کوئی بھی ایک جامع بیان نہیں دے سکتا کہ موسیقی مکمل طور پر حرام ہے، اور کوئی بھی ایک جامع بیان نہیں دے سکتا کہ یہ حلال ہے. اور مسلمانوں کو کبھی بھی اس چیز میں بہت زیادہ نہیں جانا کیونکہ اب آپ دیکھ سکتے ہیں کہ مغرب میں لوگ ہر وقت موسیقی سنتے ہیں، ان کے پاس نہیں ہے، وہ ہمیشہ پلگ ان ہیں. وہ اپنی گاڑیوں میں جاتے ہیں، وہ موسیقی کو چالو کرتے ہیں. وہ چلتے ہیں، وہ اپنے کانوں میں رکھتے ہیں، اور وہ اپنی موسیقی سنتے ہیں، اور لوگوں کے پاس لمبی پلے لسٹس ہیں، وہ ان چیزوں پر بہت پیسہ خرچ کرتے ہیں، اور اس طرح وہ کھو گئے ہیں، ان کے پاس سوچنے کے لئے زیادہ مفت وقت نہیں ہے کیونکہ ان کی زندگی ایسی آواز سے بھری ہوئی ہے جو ان کے پاس پہلے کبھی نہیں تھی، اور وہ ان کا استعمال بہت مؤثر طریقے سے کر رہے ہیں. اور ہم، بدقسمتی سے، حق کے لوگ، ان کا مؤثر طریقے سے استعمال نہیں کر رہے ہیں بالکل. اور اللہ تعالیٰ فرماتا ہے، "جو اوزار سے تم لڑ رہے ہو ان سے لڑو. اس زمانے کا جہاد کانوں کے درمیان ہے. اس زمانے کا جہاد کانوں کے درمیان ہے. میدانِ جنگ انسانوں کا ذہن ہے اور اس کے علاوہ دل بھی. ".
|
translation.py
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import openai
|
3 |
+
|
4 |
+
import locale
|
5 |
+
locale.getpreferredencoding = lambda: "UTF-8"
|
6 |
+
|
7 |
+
import dl_translate as dlt
|
8 |
+
from deep_translator import GoogleTranslator
|
9 |
+
|
10 |
+
from languages import LANGUAGES
|
11 |
+
|
12 |
+
|
13 |
+
OPENAI_API_KEY = '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'
|
14 |
+
OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions'
|
15 |
+
openai.api_key = OPENAI_API_KEY
|
16 |
+
|
17 |
+
class Translation:
|
18 |
+
|
19 |
+
def __init__(self, transcript_dict, source_lang, target_lang, output_path):
|
20 |
+
self.transcript_dict = transcript_dict
|
21 |
+
self.output_path = os.path.join(os.getcwd(), output_path)
|
22 |
+
|
23 |
+
# Languages
|
24 |
+
self.source_lang = source_lang # Whisper Detected Language
|
25 |
+
self.target_lang = target_lang
|
26 |
+
|
27 |
+
# Transcript
|
28 |
+
self.transcript = transcript_dict['text'].strip()
|
29 |
+
self.subtitles = self.__get_subtitles()
|
30 |
+
|
31 |
+
# Translation Model
|
32 |
+
nllb_model = 'facebook/nllb-200-distilled-600M'
|
33 |
+
# nllb_model = 'facebook/nllb-200-1.3B'
|
34 |
+
# nllb_model = 'facebook/nllb-200-3.3B'
|
35 |
+
# nllb_model = 'facebook/nllb-moe-54b'
|
36 |
+
self.nllb = dlt.TranslationModel(nllb_model)
|
37 |
+
|
38 |
+
def __get_subtitles(self):
|
39 |
+
'''
|
40 |
+
Returns the subtitles from transcript dictionary
|
41 |
+
'''
|
42 |
+
|
43 |
+
subtitles = []
|
44 |
+
for s in self.transcript_dict['segments']:
|
45 |
+
segment = {
|
46 |
+
'start': s['start'],
|
47 |
+
'end': s['end'],
|
48 |
+
'text': s['text'].strip()
|
49 |
+
}
|
50 |
+
subtitles.append(segment)
|
51 |
+
return subtitles
|
52 |
+
|
53 |
+
def __correct_punctuation_gpt(self):
|
54 |
+
'''
|
55 |
+
Corrects the Punctuation from GPT
|
56 |
+
'''
|
57 |
+
|
58 |
+
system_prompt = """
|
59 |
+
You are a helpful NLP assistant.
|
60 |
+
Your task is to identify language of the provided text,
|
61 |
+
correct any spelling discrepancies in the transcribed text
|
62 |
+
as well as add punctuation in the multilingual text if they are missing.
|
63 |
+
Only add necessary punctuation such as periods, commas, and capitalization,
|
64 |
+
and use only the context provided.
|
65 |
+
|
66 |
+
You response should be as follows:
|
67 |
+
Corrected Text:
|
68 |
+
Here goes the corrected text with punctuation.
|
69 |
+
"""
|
70 |
+
|
71 |
+
user_prompt = f"""
|
72 |
+
Here is the text:
|
73 |
+
{self.transcript}
|
74 |
+
"""
|
75 |
+
|
76 |
+
response = openai.ChatCompletion.create(
|
77 |
+
model="gpt-3.5-turbo",
|
78 |
+
messages=[
|
79 |
+
{"role": "system", "content": system_prompt},
|
80 |
+
{"role": "user", "content": user_prompt},
|
81 |
+
]
|
82 |
+
)
|
83 |
+
|
84 |
+
text = response.choices[0].message.content.replace('Corrected Text:\n', '')
|
85 |
+
return text
|
86 |
+
|
87 |
+
def get_translated_transcript(self):
|
88 |
+
'''
|
89 |
+
Translates the transcript into required language
|
90 |
+
'''
|
91 |
+
|
92 |
+
# Correcting Punctuation using GPT
|
93 |
+
transcript = self.__correct_punctuation_gpt()
|
94 |
+
|
95 |
+
# Splitting Text into Sentences
|
96 |
+
if self.source_lang in ['ar', 'ur']:
|
97 |
+
splitter = '۔'
|
98 |
+
else:
|
99 |
+
splitter = '.'
|
100 |
+
sentences = transcript.split(splitter)
|
101 |
+
|
102 |
+
# Getting Translation using NLLB
|
103 |
+
translated_transcript = ''
|
104 |
+
for sentence in sentences:
|
105 |
+
translated_sentence = self.nllb.translate(sentence, source=LANGUAGES[self.source_lang], target=LANGUAGES[self.target_lang])
|
106 |
+
translated_transcript += translated_sentence + splitter + ' '
|
107 |
+
# print('Text:', sentence)
|
108 |
+
# print('Text:', translated_sentence)
|
109 |
+
# print()
|
110 |
+
translated_transcript = translated_transcript.strip()
|
111 |
+
|
112 |
+
return translated_transcript
|
113 |
+
|
114 |
+
def get_translated_subtitles(self):
|
115 |
+
'''
|
116 |
+
Translates the subtitles into required language
|
117 |
+
'''
|
118 |
+
|
119 |
+
# Creating copy of Transcript Dictionary
|
120 |
+
subtitles = self.subtitles.copy()
|
121 |
+
|
122 |
+
# Creating Instance for Google Translator
|
123 |
+
gt = GoogleTranslator(source='auto', target=self.target_lang)
|
124 |
+
for i, s in enumerate(subtitles):
|
125 |
+
subtitles[i]['text'] = gt.translate(text=s['text'])
|
126 |
+
|
127 |
+
return subtitles
|