Рассмотрим как пример код для выгрузки субтитров и скачивания видео с YouTube.
1. Переходим на https://replit.com и регистрируемся.
2. Далее в главном меню нажимаем Create app
3. Выбираем Choose a Template
4. Находим Python
5. Называем и нажимаем Create App
6. У вас должно появиться окно с файлом main.py
7. На этой же странице создаем папку requirements.txt
и вписываем туда эти строки для полноценной работы кода:
google-api-python-client
google-auth-httplib2
google-auth-oauthlib
Flask
requests
pydub
browser-cookie3
yt-dlp
8. Далее создаем файл replit.nix
и туда вписываем этот код:
{ pkgs }: {
deps = [
pkgs.ffmpeg-full
pkgs.python311Full
pkgs.ffmpeg
];
}
9. После создаем папку cookies.txt, куда мы сложим наши cookies из браузера для обхода спам защиты YouTube.
Для получения cookies:
10. Нажимаем Export All Cookies и содержимое скачанного файла копируем в папку cookies.txt, которую мы создали ранее на replit
11. После идём обратно в main.py и вставляем наш код
1import os
2import re
3import urllib.parse
4import glob
5
6from flask import Flask, request, send_file
7import yt_dlp
8
9app = Flask(__name__)
10app.url_map.strict_slashes = False
11
12# ===== CONFIGURATION =====
13WEBHOOK_SECRET = "s3cr3t-k3y-123" # Замените на свой реальный секрет
14
15
16# ===== VIDEO ID EXTRACTION =====
17def extract_video_id(url_or_id):
18 """
19 Извлекает ID YouTube-видео из ссылки (включая youtu.be, youtube.com).
20 Если входная строка не URL, а просто ID, возвращает как есть.
21 """
22 if not url_or_id.startswith("http"):
23 return url_or_id
24
25 parsed = urllib.parse.urlparse(url_or_id)
26 if parsed.hostname == "youtu.be":
27 # Пример: https://youtu.be/ABCD
28 return parsed.path.lstrip("/")
29 if parsed.hostname in ["www.youtube.com", "youtube.com"]:
30 qs = urllib.parse.parse_qs(parsed.query)
31 if "v" in qs:
32 return qs["v"][0]
33 path = parsed.path.split("/")
34 if "embed" in path or "v" in path:
35 return path[-1]
36 # Если логика не сработала, возвращаем исходную строку
37 return url_or_id
38
39
40# ===== SUBTITLE PROCESSING (CLEANING) =====
41def clean_subtitle_content(content):
42 """
43 Очищает текст субтитров (WebVTT/SRT) от таймкодов, тегов, лишних пробелов и т.д.
44 Возвращает конечную строку.
45 """
46 # 1) Удаляем WEBVTT заголовок
47 cleaned = re.sub(r'WEBVTT.*?\n\n', '', content, flags=re.DOTALL | re.IGNORECASE)
48 # 2) Удаляем таймкоды (00:00:00.000 --> 00:00:05.000 и т.д.)
49 cleaned = re.sub(r'\d{2}:\d{2}:\d{2}\.\d{3}.*\n', '', cleaned)
50 # 3) Удаляем метки align:start position:% и тэги <...>
51 cleaned = re.sub(r'align:start position:\d+%', '', cleaned, flags=re.IGNORECASE)
52 cleaned = re.sub(r'<[^>]+>', ' ', cleaned)
53
54 lines = []
55 last_line = ''
56 for line in cleaned.split('\n'):
57 line = line.strip()
58 if not line:
59 continue
60
61 # Удаляем лишние пробелы перед пунктуацией и двойные пробелы
62 line = re.sub(r'\s+([,.!?])', r'\1', line)
63 line = re.sub(r'\s*-\s*', '-', line)
64 line = re.sub(r'\s{2,}', ' ', line)
65
66 # Убираем повторяющиеся строки
67 if line not in last_line:
68 lines.append(line)
69 last_line = line
70
71 final_text = ' '.join(lines)
72 return final_text.strip()
73
74
75# ===== SUBTITLE DOWNLOAD =====
76def download_subtitles(video_id):
77 """
78 Пытается скачать субтитры (англ/рус) для данного video_id.
79 Возвращает путь к субтитрам или None, если нет субтитров.
80 """
81 url = f"https://www.youtube.com/watch?v={video_id}"
82 ydl_opts = {
83 'writesubtitles': True,
84 'writeautomaticsub': True,
85 'subtitleslangs': ['en', 'ru'], # приоритетные языки
86 'skip_download': True,
87 'outtmpl': f"{video_id}",
88 'quiet': True,
89 }
90
91 try:
92 with yt_dlp.YoutubeDL(ydl_opts) as ydl:
93 ydl.download([url])
94 except yt_dlp.utils.DownloadError as e:
95 if 'No subtitles found' in str(e):
96 return None
97 raise
98
99 # Ищем субтитры .vtt или .srt
100 subtitle_files = glob.glob(f"{video_id}.*.vtt") + glob.glob(f"{video_id}.*.srt")
101
102 # Смотрим сначала en, потом ru
103 for lang in ['en', 'ru']:
104 for sub_file in subtitle_files:
105 if f'.{lang}.' in sub_file:
106 return sub_file
107 return None
108
109
110# ===== AUDIO DOWNLOAD =====
111def download_audio_file(video_id):
112 """
113 Скачивает YouTube audio (bestaudio) и конвертирует в mp3.
114 Возвращает путь к mp3-файлу.
115 """
116 url = f"https://www.youtube.com/watch?v={video_id}"
117 ydl_opts = {
118 'format': 'bestaudio/best',
119 'outtmpl': f"{video_id}.%(ext)s",
120 'postprocessors': [{
121 'key': 'FFmpegExtractAudio',
122 'preferredcodec': 'mp3',
123 'preferredquality': '192',
124 }],
125 'quiet': True,
126 }
127
128 with yt_dlp.YoutubeDL(ydl_opts) as ydl:
129 ydl.download([url])
130
131 return f"{video_id}.mp3"
132
133
134# ===== FLASK APP =====
135@app.route('/audio', methods=['POST'])
136def handle_audio():
137 """
138 Принимает JSON: { "video_url": "https://youtube.com/watch?v=..." }
139 1. Проверяет заголовок X-API-Key
140 2. Извлекает video_id из ссылки
141 3. Пытается скачать субтитры -> возвращает текст, иначе скачивает mp3 -> возвращает файл.
142 """
143 # Проверяем ключ
144 if request.headers.get('X-API-Key') != WEBHOOK_SECRET:
145 return "Unauthorized", 401
146
147 # Парсим тело запроса
148 try:
149 data = request.get_json(force=True)
150 video_url = data.get('video_url', '')
151 if not video_url:
152 return "Missing 'video_url'", 400
153 except Exception as e:
154 return f"Invalid request: {str(e)}", 400
155
156 # Извлекаем ID из ссылки
157 try:
158 video_id = extract_video_id(video_url)
159
160 # Пытаемся скачать субтитры
161 subtitle_path = download_subtitles(video_id)
162 if subtitle_path:
163 with open(subtitle_path, 'r', encoding='utf-8') as f:
164 raw_content = f.read()
165 cleaned = clean_subtitle_content(raw_content)
166 return cleaned, 200, {'Content-Type': 'text/plain'}
167
168 # Иначе качаем аудио
169 audio_path = download_audio_file(video_id)
170 if not os.path.exists(audio_path):
171 return "Audio generation failed", 500
172
173 return send_file(audio_path, as_attachment=True, mimetype='audio/mpeg')
174
175 except Exception as e:
176 return f"Processing error: {str(e)}", 500
177
178
179@app.route('/')
180def home():
181 return "YouTube Content Server - POST to /audio with JSON {video_url}", 200
182
183
184if __name__ == '__main__':
185 # Просто запускаем Flask, без keep-alive
186 port = int(os.environ.get("PORT", 8080))
187 app.run(host='0.0.0.0', port=port, debug=False)
12. Запускаем приложение и ждём следующую страницу:
13. Далее копируем ссылку на наше приложение и в конце url ссылки добавляем слово /audio
Должно получиться вот так: https://adf50a07-ff24-48e6-bb14-5e1d16af97b2-00-1mxttap45ya2u.riker.replit.dev/audio
Готово. Теперь на эту ссылку можно отправлять http запросы с n8n.
Внимание! Для того, чтобы ваш код работал постоянно, даже когда ваш компьютер выключен, вам нужно захостить ваш код через кнопку Deploy.
Эта функция доступна только через платную подписку.