diff --git a/bot.py b/bot.py index 855f530..7818e79 100644 --- a/bot.py +++ b/bot.py @@ -562,18 +562,18 @@ async def process_queue_item(item: QueueItem): video_file = open(video_path, 'rb') caption = get_text(item.locale, 'caption', bot_username=TELEGRAM_BOT_USERNAME) + caption += f"\n\n{item.url}" # Определяем имя файла для отправки video_filename = Path(video_path).name - # Отправляем как документ, чтобы Telegram НЕ сжимал видео - # (reply_video сжимает, что приводит к потере качества и одинаковому размеру) + # Отправляем как документ — Telegram сам определит тип по расширению await item.original_message.reply_document( document=video_file, filename=video_filename, caption=caption, - read_timeout=600, # 10 минут на ответ от Telegram - write_timeout=600, # 10 минут на отправку файла + read_timeout=600, + write_timeout=600, connect_timeout=60, pool_timeout=60 ) diff --git a/youtube-downloader/app.py b/youtube-downloader/app.py index f7333ec..b6988aa 100644 --- a/youtube-downloader/app.py +++ b/youtube-downloader/app.py @@ -295,12 +295,20 @@ def download_youtube_video(url: str, max_retries: int = 3, format_id: str | None combined_fallback = ['best[ext=mp4]/best', 'best'] requested_height = None # высота, запрошенная пользователем + is_audio_only = False if format_id: is_specific_code = not ('[' in format_id or ']' in format_id) + # Если format_id запрашивает только аудио, не подмешиваем видео-форматы + # и не валидируем наличие видео-потока + first_selector = format_id.split('/')[0] + is_audio_only = 'bestaudio' in first_selector requested_height = _extract_height_from_format_id(format_id) - if requested_height is not None: + if is_audio_only: + format_options = [format_id] + logger.info(f"[DOWNLOAD] Аудио-only режим, format_id: {format_id}") + elif requested_height is not None: # Конкретный format_id (из /formats) ставим ПЕРВЫМ — # он точно указывает выбранные пользователем format codes. # Height-ограниченный селектор идёт как fallback @@ -354,17 +362,16 @@ def download_youtube_video(url: str, max_retries: int = 3, format_id: str | None logger.info(f"[DOWNLOAD] Попытка {attempt + 1}: успешно скачано с форматом {format_option}") - # Проверяем, что файл содержит видео-поток, а не только аудио - # (yt-dlp c allow_unplayable_formats может скачать av01 формат - # и отказаться от мержа, вернув только аудио) - downloaded = _find_latest_downloaded() - if downloaded and not _file_has_video_stream(downloaded): - logger.warning(f"[DOWNLOAD] Файл {downloaded.name} не содержит видео-потока (только аудио). Удаляем и пробуем следующий формат...") - try: - downloaded.unlink() - except Exception: - pass - continue + # Проверяем, что файл содержит видео-поток (только для видео-форматов) + if not is_audio_only: + downloaded = _find_latest_downloaded() + if downloaded and not _file_has_video_stream(downloaded): + logger.warning(f"[DOWNLOAD] Файл {downloaded.name} не содержит видео-потока (только аудио). Удаляем и пробуем следующий формат...") + try: + downloaded.unlink() + except Exception: + pass + continue download_success = True break @@ -392,14 +399,15 @@ def download_youtube_video(url: str, max_retries: int = 3, format_id: str | None ydl.download([url]) logger.info(f"[DOWNLOAD] Попытка {attempt + 1}: успешно скачано без cookies") - downloaded = _find_latest_downloaded() - if downloaded and not _file_has_video_stream(downloaded): - logger.warning(f"[DOWNLOAD] Файл {downloaded.name} без видео-потока. Удаляем и пробуем следующий формат...") - try: - downloaded.unlink() - except Exception: - pass - continue + if not is_audio_only: + downloaded = _find_latest_downloaded() + if downloaded and not _file_has_video_stream(downloaded): + logger.warning(f"[DOWNLOAD] Файл {downloaded.name} без видео-потока. Удаляем и пробуем следующий формат...") + try: + downloaded.unlink() + except Exception: + pass + continue download_success = True cookies_valid = False # Отключаем cookies для следующих попыток @@ -668,10 +676,10 @@ def get_youtube_formats(url: str) -> list[dict]: 'filesize_mb': round(total_size / 1024 / 1024, 1) if total_size else None, }) - # Добавляем аудиодорожку + # Добавляем аудиодорожку (M4A в приоритете — Telegram поддерживает только MP3/M4A для reply_audio) if best_audio_info['size']: result.append({ - 'format_id': 'bestaudio/best', + 'format_id': 'bestaudio[ext=m4a]/bestaudio[ext=mp3]/bestaudio/best', 'label': f"Audio only ({best_audio_info['ext']})", 'quality': 'audio', 'ext': best_audio_info['ext'], @@ -871,15 +879,19 @@ def download_stream(): # Безопасное имя файла без кириллицы для заголовка safe_filename = video_path.name.encode('ascii', 'ignore').decode('ascii') or 'youtube_video.mp4' - if not safe_filename.endswith(('.mp4', '.webm', '.mkv')): + if not safe_filename.endswith(('.mp4', '.webm', '.mkv', '.m4a', '.mp3')): safe_filename = 'youtube_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' + # Определяем content-type по реальному расширению файла + ext = video_path.suffix.lower() + content_type_map = { + '.webm': 'video/webm', + '.mkv': 'video/x-matroska', + '.mp4': 'video/mp4', + '.m4a': 'audio/mp4', + '.mp3': 'audio/mpeg', + } + content_type = content_type_map.get(ext, 'video/mp4') logger.info(f"[REQUEST {request_id}] Отправляем файл: {safe_filename}, Content-Type: {content_type}, размер: {len(video_data)} байт") diff --git a/youtube-downloader/youtube_cookies.txt b/youtube-downloader/youtube_cookies.txt index cb68911..393ef26 100644 --- a/youtube-downloader/youtube_cookies.txt +++ b/youtube-downloader/youtube_cookies.txt @@ -27,14 +27,14 @@ .youtube.com TRUE / FALSE 1776287761000 ST-yve142 session_logininfo=AFmmF2swRQIgZPfEOdmfC8u5sHvE1aOagKEvp5rRUe5hRUeLiYmxLDwCIQDqFIR59yZ_aBb5BLYSpK7LGdJ6YZqnh32USuOyMZTC5g%3AQUQ3MjNmd0ZzX01fTjViQ2kzMDJEWG5Ed09zMGF1TlhJcm81YWt3WWdKS2RCZkY3Z2NmMVhudUF4MFVZdFlHd0YtaEU0R3VHNHQ3VmFSZHdfR1RIcnBJNUtXeWhKWVVScE1ZcXNJdzRfdkFGVi1lZzY2dWxCcVVGZ0FPSjNzVmFjTVg1YTBYS0xBajEzU1REM3dnbUc5U3E3NHVtLVRLLXRn .youtube.com TRUE / TRUE 1807824503515 __Secure-1PSIDTS sidts-CjUBWhotCSAL5EMsgNfc0JD8UVvU5vyCYbx9ZFc0Nnry9Qc7YHRzl6a7o8Zm6bPYHoFyKALKlBAA .youtube.com TRUE / TRUE 1807824503517 __Secure-3PSIDTS sidts-CjUBWhotCSAL5EMsgNfc0JD8UVvU5vyCYbx9ZFc0Nnry9Qc7YHRzl6a7o8Zm6bPYHoFyKALKlBAA -.youtube.com TRUE / TRUE 1793313256 VISITOR_INFO1_LIVE vFr43YvHJaE -.youtube.com TRUE / TRUE 1793313256 VISITOR_PRIVACY_METADATA CgJSVRIEGgAgMg%3D%3D +.youtube.com TRUE / TRUE 1793314544 VISITOR_INFO1_LIVE vFr43YvHJaE +.youtube.com TRUE / TRUE 1793314544 VISITOR_PRIVACY_METADATA CgJSVRIEGgAgMg%3D%3D .youtube.com TRUE / FALSE 1776288519000 ST-tladcw session_logininfo=AFmmF2swRQIgZPfEOdmfC8u5sHvE1aOagKEvp5rRUe5hRUeLiYmxLDwCIQDqFIR59yZ_aBb5BLYSpK7LGdJ6YZqnh32USuOyMZTC5g%3AQUQ3MjNmd0ZzX01fTjViQ2kzMDJEWG5Ed09zMGF1TlhJcm81YWt3WWdKS2RCZkY3Z2NmMVhudUF4MFVZdFlHd0YtaEU0R3VHNHQ3VmFSZHdfR1RIcnBJNUtXeWhKWVVScE1ZcXNJdzRfdkFGVi1lZzY2dWxCcVVGZ0FPSjNzVmFjTVg1YTBYS0xBajEzU1REM3dnbUc5U3E3NHVtLVRLLXRn .youtube.com TRUE / FALSE 0 PREF tz=UTC&f7=100&f6=40000000&hl=en .youtube.com TRUE / FALSE 1776288527000 ST-xuwub9 session_logininfo=AFmmF2swRQIgZPfEOdmfC8u5sHvE1aOagKEvp5rRUe5hRUeLiYmxLDwCIQDqFIR59yZ_aBb5BLYSpK7LGdJ6YZqnh32USuOyMZTC5g%3AQUQ3MjNmd0ZzX01fTjViQ2kzMDJEWG5Ed09zMGF1TlhJcm81YWt3WWdKS2RCZkY3Z2NmMVhudUF4MFVZdFlHd0YtaEU0R3VHNHQ3VmFSZHdfR1RIcnBJNUtXeWhKWVVScE1ZcXNJdzRfdkFGVi1lZzY2dWxCcVVGZ0FPSjNzVmFjTVg1YTBYS0xBajEzU1REM3dnbUc5U3E3NHVtLVRLLXRn -.youtube.com TRUE / FALSE 1809297256 SIDCC AKEyXzVsS5YLUQD8z9C1v-mL2JIyS_lqX6qpnZKQ_AFrB5WfKI8t61IDvWwihKLswvR3ya_Y2JOP -.youtube.com TRUE / TRUE 1809297256 __Secure-1PSIDCC AKEyXzXDaKBNexbPjEPwCB8IDGZRrPTCTOVNDWgBRtsKv5XcaCCg5JxpeRXlk2gX4lidlrONyC52 -.youtube.com TRUE / TRUE 1809297256 __Secure-3PSIDCC AKEyXzUF0D1vjEN7XqU2ReXIRFuti0YZjmliSwyRcCUSq5rUlGmYoYmzjSngu8HtDmEiioigkUI9 +.youtube.com TRUE / FALSE 1809298544 SIDCC AKEyXzVa7lSTdOSv8wBZg33qEiiwbNCETtCNC9xssuu_BPPxNep2Lc2negI0NHWARFHb_MwD8V_c +.youtube.com TRUE / TRUE 1809298544 __Secure-1PSIDCC AKEyXzX8DfBF1L6BIhnm19j-ZBSyE16TpQVvn6vIBB_zA5dma1mjRuTuWTlS3ut3feYm1YMhme8q +.youtube.com TRUE / TRUE 1809298544 __Secure-3PSIDCC AKEyXzUfC0IlqutC5s9_cf3HrI_S4F8ui7NJPayGnr2TMOS5u0EuoUVZaKgJm6vklINRtl0oO9hR .youtube.com TRUE / FALSE 1776288585000 ST-3opvp5 session_logininfo=AFmmF2swRQIgZPfEOdmfC8u5sHvE1aOagKEvp5rRUe5hRUeLiYmxLDwCIQDqFIR59yZ_aBb5BLYSpK7LGdJ6YZqnh32USuOyMZTC5g%3AQUQ3MjNmd0ZzX01fTjViQ2kzMDJEWG5Ed09zMGF1TlhJcm81YWt3WWdKS2RCZkY3Z2NmMVhudUF4MFVZdFlHd0YtaEU0R3VHNHQ3VmFSZHdfR1RIcnBJNUtXeWhKWVVScE1ZcXNJdzRfdkFGVi1lZzY2dWxCcVVGZ0FPSjNzVmFjTVg1YTBYS0xBajEzU1REM3dnbUc5U3E3NHVtLVRLLXRn .youtube.com TRUE / TRUE 0 YSC KTvrS45hA30 .instagram.com TRUE / TRUE 1801240452128 datr hGdNaS-QqakSYV8X2eqVTIyA