""" TikTok Video Downloader Service Отдельный микросервис для скачивания видео с TikTok """ import os import logging import re import uuid from pathlib import Path from flask import Flask, request, jsonify from flask_cors import CORS import yt_dlp # Настройка логирования logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) app = Flask(__name__) CORS(app) # Директория для временных файлов DOWNLOADS_DIR = Path('downloads') DOWNLOADS_DIR.mkdir(exist_ok=True) # User-Agent для TikTok USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' def download_tiktok_video(url: str, max_retries: int = 3) -> Path: """Скачивает видео с TikTok""" last_error = None for attempt in range(max_retries): try: # Получаем информацию о видео ydl_opts_info = { 'quiet': False, 'no_warnings': False, 'user_agent': USER_AGENT, 'socket_timeout': 30, 'http_headers': { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', }, } with yt_dlp.YoutubeDL(ydl_opts_info) as ydl: info = ydl.extract_info(url, download=False) video_title = info.get('title', 'tiktok_video') logger.info(f"TikTok: получена информация о видео: {video_title}") # Безопасное имя файла safe_title = re.sub(r'[<>:"/\\|?*]', '', video_title)[:80] output_template = str(DOWNLOADS_DIR / f'{uuid.uuid4()}_{safe_title}.%(ext)s') # Скачиваем видео ydl_opts_download = { 'format': 'best[ext=mp4]/best', 'outtmpl': output_template, 'quiet': False, 'no_warnings': False, 'user_agent': USER_AGENT, 'socket_timeout': 30, 'http_headers': { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', }, } logger.info(f"TikTok: начинаем скачивание (попытка {attempt + 1}/{max_retries})") with yt_dlp.YoutubeDL(ydl_opts_download) as ydl: ydl.download([url]) # Находим скачанный файл downloaded_files = list(DOWNLOADS_DIR.glob('*')) if downloaded_files: downloaded_files.sort(key=lambda x: x.stat().st_mtime, reverse=True) return downloaded_files[0] else: raise Exception("Файл не был найден после скачивания") except Exception as e: last_error = e logger.warning(f"TikTok: попытка {attempt + 1}/{max_retries} не удалась: {e}") if attempt < max_retries - 1: import time time.sleep((attempt + 1) * 2) raise last_error or Exception("Неизвестная ошибка при скачивании с TikTok") @app.route('/health', methods=['GET']) def health(): """Health check endpoint""" return jsonify({'status': 'ok', 'service': 'tiktok-downloader'}), 200 @app.route('/download/stream', methods=['POST']) def download_stream(): """Скачивает видео с TikTok и возвращает бинарные данные""" try: data = request.get_json() if not data or 'url' not in data: return jsonify({'error': 'URL is required'}), 400 url = data['url'] logger.info(f"Получен запрос на скачивание (stream): {url}") # Проверяем, что это TikTok URL if 'tiktok.com' not in url: return jsonify({'error': 'Only TikTok URLs are supported'}), 400 # Скачиваем видео video_path = download_tiktok_video(url) logger.info(f"Видео скачано: {video_path}") # Читаем файл и отправляем with open(video_path, 'rb') as f: video_data = f.read() # Безопасное имя файла для заголовка safe_filename = video_path.name.encode('ascii', 'ignore').decode('ascii') or 'tiktok_video.mp4' if not safe_filename.endswith(('.mp4', '.webm', '.mkv')): safe_filename = 'tiktok_video.mp4' # Определяем content-type content_type = 'video/mp4' if video_path.suffix == '.webm': content_type = 'video/webm' elif video_path.suffix == '.mkv': content_type = 'video/x-matroska' # Удаляем временный файл video_path.unlink() return video_data, 200, { 'Content-Type': content_type, 'Content-Disposition': f'attachment; filename="{safe_filename}"' } except Exception as e: logger.error(f"Ошибка при скачивании: {e}") return jsonify({'error': str(e)}), 500 if __name__ == '__main__': port = int(os.getenv('PORT', 5000)) host = os.getenv('HOST', '0.0.0.0') logger.info(f"Запуск TikTok Downloader сервиса на {host}:{port}") app.run(host=host, port=port, debug=False)