diff --git a/youtube-downloader/.gitignore b/youtube-downloader/.gitignore new file mode 100644 index 0000000..a095618 --- /dev/null +++ b/youtube-downloader/.gitignore @@ -0,0 +1,9 @@ +*.log + +# Временные файлы загрузок +downloads/ + +# Файл cookies YouTube (секрет, не коммитить) +youtube_cookies.txt + + diff --git a/youtube-downloader/app.py b/youtube-downloader/app.py index d8136fe..d1ab7bb 100644 --- a/youtube-downloader/app.py +++ b/youtube-downloader/app.py @@ -32,29 +32,48 @@ def _safe_filename(title: str) -> str: return str(DOWNLOADS_DIR / f'{uuid.uuid4()}_{safe_title}.%(ext)s') +def _is_valid_cookies_file(cookies_path: Path) -> bool: + """Проверяет, что файл cookies существует и содержит данные (не только заголовки)""" + if not cookies_path.exists(): + return False + + try: + with open(cookies_path, 'r', encoding='utf-8', errors='ignore') as f: + lines = [line.strip() for line in f.readlines() if line.strip() and not line.strip().startswith('#')] + # Проверяем, что есть хотя бы одна строка с данными cookie + return len(lines) > 0 + except Exception as e: + logger.warning(f"Ошибка при проверке файла cookies: {e}") + return False + + def download_youtube_video(url: str, max_retries: int = 3) -> Path: """Скачивает видео с YouTube - используем cookies для обхода блокировок""" cookies_file = os.getenv('YOUTUBE_COOKIES_FILE', 'youtube_cookies.txt') cookies_file_path = Path(cookies_file) - if not cookies_file_path.exists(): - logger.info(f"YouTube: файл cookies не найден ({cookies_file_path}). Работаем без cookies. " - f"Для лучшей работы рекомендуется добавить cookies через скрипт get_youtube_cookies_local.sh") + cookies_valid = _is_valid_cookies_file(cookies_file_path) + if not cookies_valid: + logger.warning(f"YouTube: файл cookies не найден или невалиден ({cookies_file_path}). " + f"Работаем без cookies. Для лучшей работы рекомендуется обновить cookies через скрипт get_youtube_cookies.sh") 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' last_error = None for attempt in range(max_retries): try: + # Определяем, это Shorts или обычное видео + is_shorts = '/shorts/' in url + # Базовые настройки для получения информации ydl_opts_info = { 'quiet': False, 'no_warnings': False, 'user_agent': user_agent, - 'socket_timeout': 30, + 'socket_timeout': 60, # Увеличиваем таймаут 'extractor_args': { 'youtube': { - 'player_client': ['android', 'web'], + 'player_client': ['android', 'web'] if not is_shorts else ['android', 'ios', 'web'], 'player_skip': ['webpage'], }, }, @@ -67,10 +86,12 @@ def download_youtube_video(url: str, max_retries: int = 3) -> Path: }, } - # Если есть файл с cookies, используем его - if cookies_file_path.exists(): + # Если есть валидный файл с cookies, используем его + if cookies_valid: ydl_opts_info['cookiefile'] = str(cookies_file_path.absolute()) - logger.info(f"YouTube: используем cookies из {cookies_file_path}") + logger.info(f"YouTube: используем cookies из {cookies_file_path.absolute()} (попытка {attempt + 1})") + else: + logger.info(f"YouTube: работаем без cookies (попытка {attempt + 1})") with yt_dlp.YoutubeDL(ydl_opts_info) as ydl: info = ydl.extract_info(url, download=False) @@ -84,10 +105,10 @@ def download_youtube_video(url: str, max_retries: int = 3) -> Path: 'quiet': False, 'no_warnings': False, 'user_agent': user_agent, - 'socket_timeout': 30, + 'socket_timeout': 60, # Увеличиваем таймаут 'extractor_args': { 'youtube': { - 'player_client': ['android', 'web'], + 'player_client': ['android', 'web'] if not is_shorts else ['android', 'ios', 'web'], 'player_skip': ['webpage'], }, }, @@ -100,11 +121,11 @@ def download_youtube_video(url: str, max_retries: int = 3) -> Path: }, } - # Если есть файл с cookies, используем его для скачивания - if cookies_file_path.exists(): + # Если есть валидный файл с cookies, используем его для скачивания + if cookies_valid: ydl_opts_download['cookiefile'] = str(cookies_file_path.absolute()) - logger.info(f"YouTube: начинаем скачивание (попытка {attempt + 1}/{max_retries})") + logger.info(f"YouTube: начинаем скачивание (попытка {attempt + 1}/{max_retries}, Shorts: {is_shorts})") with yt_dlp.YoutubeDL(ydl_opts_download) as ydl: ydl.download([url]) @@ -118,7 +139,16 @@ def download_youtube_video(url: str, max_retries: int = 3) -> Path: except Exception as e: last_error = e - logger.warning(f"YouTube: попытка {attempt + 1}/{max_retries} не удалась: {e}") + error_str = str(e) + logger.warning(f"YouTube: попытка {attempt + 1}/{max_retries} не удалась: {error_str}") + + # Если ошибка связана с cookies и они были использованы, попробуем без cookies на следующей попытке + if 'cookies' in error_str.lower() or 'bot' in error_str.lower() or 'sign in' in error_str.lower(): + if cookies_valid and attempt == 0: + logger.warning("YouTube: ошибка с cookies, попробуем обновить cookies или работать без них") + # На следующей попытке попробуем без cookies + cookies_valid = False + if attempt < max_retries - 1: import time time.sleep((attempt + 1) * 2) @@ -176,8 +206,22 @@ def download_stream(): } except Exception as e: - logger.error(f"Ошибка при скачивании: {e}") - return jsonify({'error': str(e)}), 500 + error_str = str(e) + logger.error(f"Ошибка при скачивании: {error_str}") + + # Улучшаем сообщение об ошибке, если проблема с cookies + if 'cookies' in error_str.lower() or 'bot' in error_str.lower() or 'sign in' in error_str.lower(): + error_msg = ( + f"{error_str}\n\n" + "💡 Совет: Cookies устарели или недействительны. " + "Обновите cookies, запустив скрипт:\n" + " ./youtube-downloader/get_youtube_cookies.sh\n" + "Затем перезапустите сервис." + ) + else: + error_msg = error_str + + return jsonify({'error': error_msg}), 500 if __name__ == '__main__': diff --git a/youtube-downloader/get_youtube_cookies.sh b/youtube-downloader/get_youtube_cookies.sh index 7dab895..6fc9e9e 100755 --- a/youtube-downloader/get_youtube_cookies.sh +++ b/youtube-downloader/get_youtube_cookies.sh @@ -48,17 +48,68 @@ if ! command -v yt-dlp &> /dev/null; then exit 1 fi -yt-dlp --cookies-from-browser "$BROWSER" --cookies "$COOKIES_FILE" --no-download https://www.youtube.com 2>&1 | head -10 +echo "" +echo "ВАЖНО: Убедитесь, что вы залогинены в YouTube в выбранном браузере!" +echo "Нажмите Enter для продолжения..." +read + +echo "" +echo "Получаю cookies из браузера $BROWSER..." +echo "Это должно занять несколько секунд..." + +# Используем простой способ: извлекаем cookies из браузера и сохраняем в файл +# Используем главную страницу YouTube (самый простой запрос) +# Таймаут 15 секунд - этого должно хватить для извлечения cookies +# --no-download - не скачивать видео +# --quiet - минимум вывода +timeout 15 yt-dlp \ + --cookies-from-browser "$BROWSER" \ + --cookies "$COOKIES_FILE" \ + --no-download \ + --quiet \ + "https://www.youtube.com" 2>&1 | head -20 + +EXIT_CODE=$? + +if [ $EXIT_CODE -eq 124 ]; then + echo "" + echo "⚠️ Процесс получения cookies превысил таймаут (20 сек)" + echo " Проверяю, был ли создан файл cookies..." +elif [ $EXIT_CODE -ne 0 ] && [ $EXIT_CODE -ne 124 ]; then + echo "" + echo "⚠️ Процесс завершился с кодом $EXIT_CODE" + echo " Проверяю, был ли создан файл cookies..." +fi if [ -f "$COOKIES_FILE" ]; then - echo "" - echo "✅ Cookies успешно сохранены в $COOKIES_FILE" - echo "" - echo "Теперь перезапустите youtube-downloader:" - echo " docker compose -f youtube-downloader/docker-compose.yml restart" + # Проверяем, что файл содержит данные (не только заголовки) + COOKIE_LINES=$(grep -v '^#' "$COOKIES_FILE" | grep -v '^$' | wc -l) + + if [ "$COOKIE_LINES" -gt 0 ]; then + echo "" + echo "✅ Cookies успешно сохранены в $COOKIES_FILE" + echo " Найдено строк с cookies: $COOKIE_LINES" + echo "" + echo "Теперь перезапустите youtube-downloader:" + echo " docker compose -f youtube-downloader/docker-compose.yml restart" + echo "" + echo "Или перезапустите все сервисы:" + echo " ./stop_all.sh && ./start_all.sh" + else + echo "" + echo "❌ Ошибка: файл cookies создан, но не содержит данных" + echo " Убедитесь, что вы залогинены в YouTube в браузере $BROWSER" + rm -f "$COOKIES_FILE" + exit 1 + fi else echo "" echo "❌ Ошибка: файл cookies не был создан" + echo "" + echo "Возможные причины:" + echo "1. Вы не залогинены в YouTube в браузере $BROWSER" + echo "2. Браузер $BROWSER не найден или недоступен" + echo "3. Проблемы с правами доступа к файлу cookies браузера" exit 1 fi