import sys
import os
import asyncio
import logging
from typing import Optional
from PySide6.QtWidgets import (
    QApplication, QWidget, QPushButton, QLabel, QLineEdit,
    QFileDialog, QVBoxLayout, QHBoxLayout, QMessageBox, QProgressBar
)
from PySide6.QtCore import Qt
import ffmpeg
import aiohttp
import tempfile
import qasync

# Настройка логирования
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Создаем обработчик вывода в консоль
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)

# Настраиваем формат выводимых сообщений
formatter = logging.Formatter('%(asctime)s - %(__name__)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

# Проверяем, добавлен ли обработчик, чтобы избежать дублирования
if not logger.handlers:
    logger.addHandler(console_handler)


class VideoToGifConverter:
    def __init__(self):  # Исправлено: добавлены двойные подчеркивания
        self.input_path: Optional[str] = None
        self.temp_files = []

    async def download_video(self, url: str) -> Optional[str]:
        """Асинхронно скачивает видео по прямой ссылке."""
        temp_dir = tempfile.mkdtemp()
        local_filename = os.path.join(temp_dir, os.path.basename(url))
        try:
            logger.info(f"Начинается загрузка видео по URL: {url}")
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as resp:
                    if resp.status != 200:
                        logger.error(f"Не удалось скачать видео: статус {resp.status}")
                        return None
                    with open(local_filename, 'wb') as f:
                        while True:
                            chunk = await resp.content.read(1024)
                            if not chunk:
                                break
                            f.write(chunk)
            logger.info(f"Видео успешно скачано: {local_filename}")
            self.temp_files.append(local_filename)
            return local_filename
        except Exception as e:
            logger.error(f"Ошибка при скачивании видео: {e}")
            return None

    async def convert_to_gif(self, input_path: str, output_path: str,
                             start_time: Optional[float] = None, duration: Optional[float] = None,
                             scale: Optional[int] = None, fps: int = 15,
                             text_overlay: Optional[str] = None,
                             progress_callback=None):
        """Конвертирует видео в GIF с указанными параметрами."""
        try:
            probe = ffmpeg.probe(input_path)
            video_stream = next(
                (stream for stream in probe['streams'] if stream['codec_type'] == 'video'),
                None
            )
            if video_stream is None:
                logger.error("Не удалось найти видеопоток.")
                return False

            input_args = {}
            if start_time is not None:
                input_args['ss'] = start_time

            if duration is not None:
                input_args['t'] = duration

            output_args = {
                'format': 'gif',
                'r': fps
            }

            vf_filters = []

            if scale is not None:
                vf_filters.append(f'scale={scale}:-1')

            if text_overlay:
                # Добавляем текст с помощью фильтров
                vf_filters.append(
                    f"drawtext=text='{text_overlay}':fontcolor=white:fontsize=24:x=(w-text_w)/2:y=(h-text_h)-10"
                )

            if vf_filters:
                output_args['vf'] = ','.join(vf_filters)

            # Настройка прогресса
            def progress_handler():
                if progress_callback:
                    progress_callback(100)  # Устанавливаем 100% по завершению

            loop = asyncio.get_event_loop()
            await loop.run_in_executor(
                None, self.run_ffmpeg, input_path, output_path, input_args, output_args
            )
            progress_handler()

            logger.info(f"GIF-файл успешно сохранен: {output_path}")
            return True
        except Exception as e:
            logger.error(f"Ошибка при конвертации видео: {e}")
            return False

    def run_ffmpeg(self, input_path, output_path, input_args, output_args):
        """Запускает команду ffmpeg с указанными аргументами."""
        try:
            stream = ffmpeg.input(input_path, **input_args)
            stream = ffmpeg.output(stream, output_path, *output_args)
            stream = ffmpeg.overwrite_output(stream)
            ffmpeg.run(stream)
        except ffmpeg.Error as e:
            logger.error(f"Ошибка FFmpeg: {e}")
            raise e

    def clean_up(self):
        """Удаляет временные файлы."""
        for file in self.temp_files:
            try:
                os.remove(file)
                logger.info(f"Удален временный файл: {file}")
            except Exception as e:
                logger.warning(f"Не удалось удалить временный файл {file}: {e}")
        self.temp_files.clear()


class MainWindow(QWidget):
    def __init__(self):  # Исправлено: добавлены двойные подчеркивания
        super().__init__()  # Исправлено: вызов конструктора родительского класса
        self.setWindowTitle('Конвертер Видео в GIF')
        self.converter = VideoToGifConverter()
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Выбор источника
        source_layout = QHBoxLayout()
        self.source_label = QLabel('Источник видео:')
        self.source_input = QLineEdit()
        self.browse_button = QPushButton('Обзор')
        self.browse_button.clicked.connect(self.browse_file)
        source_layout.addWidget(self.source_label)
        source_layout.addWidget(self.source_input)
        source_layout.addWidget(self.browse_button)
        layout.addLayout(source_layout)

        # Параметры конвертации
        params_layout = QVBoxLayout()

        self.start_label = QLabel('Время начала (сек):')
        self.start_input = QLineEdit()
        params_layout.addWidget(self.start_label)
        params_layout.addWidget(self.start_input)

        self.duration_label = QLabel('Продолжительность (сек):')
        self.duration_input = QLineEdit()
        params_layout.addWidget(self.duration_label)
        params_layout.addWidget(self.duration_input)

        self.scale_label = QLabel('Ширина (px):')
        self.scale_input = QLineEdit()
        params_layout.addWidget(self.scale_label)
        params_layout.addWidget(self.scale_input)

        self.fps_label = QLabel('FPS:')
        self.fps_input = QLineEdit('15')
        params_layout.addWidget(self.fps_label)
        params_layout.addWidget(self.fps_input)

        self.text_label = QLabel('Текст:')
        self.text_input = QLineEdit()
        params_layout.addWidget(self.text_label)
        params_layout.addWidget(self.text_input)

        layout.addLayout(params_layout)

        # Прогресс-бар
        self.progress_bar = QProgressBar()
        layout.addWidget(self.progress_bar)

        # Кнопка конвертации
        self.convert_button = QPushButton('Конвертировать')
        self.convert_button.clicked.connect(self.start_conversion)
        layout.addWidget(self.convert_button)

        self.setLayout(layout)

    def browse_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, 'Выберите видеофайл', '', 'Видео файлы (.mp4 *.avi *.mov .mkv)'
        )
        if file_path:
            self.source_input.setText(file_path)

    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def start_conversion(self):
        source = self.source_input.text().strip()
        if not source:
            QMessageBox.warning(self, 'Ошибка', 'Пожалуйста, укажите путь к видео.')
            return

        self.convert_button.setEnabled(False)

        if source.startswith('http://') or source.startswith('https://'):
            # Асинхронная загрузка видео
            asyncio.create_task(self.handle_url_conversion(source))
        else:
            if not os.path.isfile(source):
                QMessageBox.warning(self, 'Ошибка', 'Видео файл не найден.')
                self.convert_button.setEnabled(True)
                return
            asyncio.create_task(self.handle_file_conversion(source))

    async def handle_url_conversion(self, url):
        local_file = await self.converter.download_video(url)
        if local_file:
            await self.convert_video(local_file)
            self.converter.clean_up()
        else:
            QMessageBox.warning(self, 'Ошибка', 'Не удалось скачать видео.')
        self.convert_button.setEnabled(True)

    async def handle_file_conversion(self, file_path):
        await self.convert_video(file_path)
        self.convert_button.setEnabled(True)

    async def convert_video(self, input_path):
        output_path, _ = QFileDialog.getSaveFileName(
            self, 'Сохранить GIF как', '', 'GIF файлы (.gif)'
        )
        if not output_path:
            return

        try:
            start_time = float(self.start_input.text()) if self.start_input.text() else None
            duration = float(self.duration_input.text()) if self.duration_input.text() else None
            scale = int(self.scale_input.text()) if self.scale_input.text() else None
            fps = int(self.fps_input.text()) if self.fps_input.text() else 15
            text_overlay = self.text_input.text() if self.text_input.text() else None
        except ValueError:
            QMessageBox.warning(self, 'Ошибка', 'Пожалуйста, введите правильные числовые значения.')
            return

        self.progress_bar.setValue(0)

        success = await self.converter.convert_to_gif(
            input_path=input_path,
            output_path=output_path,
            start_time=start_time,
            duration=duration,
            scale=scale,
            fps=fps,
            text_overlay=text_overlay,
            progress_callback=self.update_progress
        )

        if success:
            QMessageBox.information(self, 'Успех', 'Конвертация завершена!')
            self.progress_bar.setValue(100)
        else:
            QMessageBox.warning(self, 'Ошибка', 'Произошла ошибка при конвертации.')

        self.convert_button.setEnabled(True)


def main():
    app = QApplication(sys.argv)
    loop = qasync.QEventLoop(app)
    asyncio.set_event_loop(loop)
    window = MainWindow()
    window.show()
    with loop:
        try:
            loop.run_forever()
        except KeyboardInterrupt:
            pass


if __name__ == '__main__':
    main()




При запуске этого кода.  Очень неудобный интерфейс и взаимодействие с пользователем просто ужасная! Но главная проблема в том что я даже не могу проверить работает ли он. Если вставляю ссылку видео с ютуба он не скачивает. Если нажимаю Обзор то он не видит видео файлы. У меня на рабочем столе 5 видео разных форматов, 2 из них .mp4. Но если я запускаю код, нажимаю Обзор ( У источника видео)  перехожу на рабочий стол, то он их просто напросто не видит.

Не спеши! Будь очень внимателен! Нужно сохранить весь функционал и все возможности. Скорость твоего ответа для меня не важна лишь глубина проработки и качество!  Мне нужен полностью готовый исчерпывающий и целый скрипт всего проекта целиком. Комплексного и полностью работоспособного