videoDownloadTGbot/tiktok-downloader/app.py

156 lines
5.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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)