Как после FFmpeg отправить видео в телеграм с правильным соотношением ширины и высоты?

не получается подобрать нужный формат при отправки видео в телеграм.

Суть такова, есть код, который скачивает видео с ютуба, обычным yt_dlp Но после работы ffmpeg когда видео отправляется в телеграм, оно на телефонной версии отображается вот так (Формат квадратный, и видео растянуто или наоборот сплющено) / Сорри за пример, но это было первое видео длительностью 3 минуты для теста)) введите сюда описание изображения

но если я скачиваю это видео на устройство, то оно отображается нормально, и более того, если я этоже видео отправляю в бот, то оно также отображается нормально Видно, что видео приобрело нужный формат

Так вот, проблема в следующем, как только не пытался ее решить... Но видео после скачивания и/или обработки ffmpeg при отправке в телеграм, не приобретает нужный формат.

Вот собственно сам код

import os
import yt_dlp
import uuid
import logging
from telebot.apihelper import ApiTelegramException
import subprocess

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("youtube_downloader")

class MyLogger:
    def debug(self, msg):
        if msg.startswith('[debug] '):
            logger.debug(msg)
        else:
            logger.info(msg)

    def info(self, msg):
        logger.info(msg)

    def warning(self, msg):
        logger.warning(msg)

    def error(self, msg):
        logger.error(msg)


def download_youtube_video(url: str, progress_callback=None, download_folder: str = "downloads") -> tuple[str, str]:
    """
    1) Скачиваем видео в H.264 (если доступно) по приоритету:
       (1080p itag=137 + audio=140) / (720p=136 + audio=140) / (720p=22) / (360p=18) / best
    2) Ремуксим без перекодирования, чтобы получить MP4.
    3) Сбрасываем rotate=0 и выставляем aspect=16:9 (помогает Телеграму корректно показывать).
    4) Извлекаем аудио в OGG.
    5) Возвращаем (video_path, audio_path).
    """

    os.makedirs(download_folder, exist_ok=True)
    file_id = str(uuid.uuid4())
    raw_video_path = os.path.join(download_folder, f"{file_id}.mp4")
    fixed_video_path = os.path.join(download_folder, f"{file_id}_fixed.mp4")
    audio_path = os.path.join(download_folder, f"{file_id}.ogg")

    def download_progress(d):
        if not progress_callback:
            return
        if d['status'] == 'downloading':
            try:
                total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate', 0)
                downloaded_bytes = d.get('downloaded_bytes', 0)
                if total_bytes > 0:
                    percent = (downloaded_bytes / total_bytes) * 100
                    important_stages = [0, 20, 40, 60, 80, 90, 95, 100]
                    current_stage = min(important_stages, key=lambda x: abs(x - percent))
                    if percent >= current_stage and d.get('_last_stage', -1) != current_stage:
                        try:
                            progress_callback({
                                'status': 'downloading',
                                'percent': f"{current_stage}%",
                                'speed': d.get('speed_str', 'N/A'),
                                'eta': d.get('eta_str', 'N/A')
                            })
                            d['_last_stage'] = current_stage
                        except ApiTelegramException as e:
                            if e.error_code == 429:
                                pass
                            else:
                                raise
            except Exception as e:
                logger.error(f"Ошибка в progress_callback: {e}")
        elif d['status'] == 'finished':
            if progress_callback:
                progress_callback({'status': 'merging'})

    # Шаблон: (1080p + 140) / (720p + 140) / 22 / 18 / best
    ydl_opts = {
        'format': '(137+140)/(136+140)/22/18/best',
        'outtmpl': raw_video_path,
        'logger': MyLogger(),
        'progress_hooks': [download_progress],
        # Если придёт видео/аудио отдельно — ремуксим в MP4:
        'postprocessors': [{
            'key': 'FFmpegVideoRemuxer',
            'preferedformat': 'mp4'
        }],
        'merge_output_format': 'mp4',
        'prefer_ffmpeg': True,
        'keepvideo': False
    }

    try:
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            logger.info(f"Начинаю скачивание: {url}")
            ydl.download([url])
            logger.info(f"Видео загружено: {raw_video_path}")


        cmd_fix = [
            'ffmpeg',
            '-i', raw_video_path,
            '-c', 'copy',
            '-metadata:s:v:0', 'rotate=0',
            '-aspect', '16:9',
            '-y',
            fixed_video_path
        ]
        logger.info("Правим DAR и убираем rotate...")
        subprocess.run(cmd_fix, check=True)

        # Можно удалить исходный файл, заменить на зафиксированный
        os.remove(raw_video_path)  # удаляем старый
        video_path = fixed_video_path  # теперь работаем с 'исправленным' файлом

        cmd_audio = [
            'ffmpeg',
            '-i', video_path,
            '-vn',
            '-acodec', 'libvorbis',
            '-q:a', '4',
            '-y',
            audio_path
        ]
        logger.info(f"Извлечение аудио -> {audio_path}")
        subprocess.run(cmd_audio, check=True)

        if not os.path.exists(video_path):
            raise FileNotFoundError("Видео файл не найден после фиксации.")
        if not os.path.exists(audio_path):
            raise FileNotFoundError("Аудио файл не найден после извлечения.")

        logger.info("Готово! Возвращаем пути к видео и аудио.")
        return video_path, audio_path

    except Exception as e:
        logger.error(f"Ошибка при скачивании/обработке: {e}")
        raise

собирал все это дело с чатом гпт. может кто сталкивался с похожей ситуацией?


Ответы (0 шт):