import sys
import requests
import datetime
import logging
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton, QLabel, QComboBox, QFileDialog, QMessageBox, QProgressBar, QTextEdit, QProgressDialog
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QCoreApplication
from pytube import YouTube

# Настройка логирования
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class PreviewThread(QThread):
    preview_signal = pyqtSignal(QPixmap)
    error_signal = pyqtSignal(str)

    def __init__(self, url):
        super().__init__()
        self.url = url

    def run(self):
        try:
            yt = YouTube(self.url)
            thumbnail_url = yt.thumbnail_url
            pixmap = QPixmap()
            pixmap.loadFromData(requests.get(thumbnail_url).content)
            self.preview_signal.emit(pixmap)
        except Exception as e:
            self.error_signal.emit(f'Не удалось загрузить превью: {e}')

class DownloadThread(QThread):
    progress_signal = pyqtSignal(int)
    speed_signal = pyqtSignal(str)
    time_signal = pyqtSignal(str)
    error_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, url, quality, path):
        super().__init__()
        self.url = url
        self.quality = quality
        self.path = path

    def run(self):
        try:
            yt = YouTube(self.url, on_progress_callback=self.on_progress)
            stream = yt.streams.filter(res=self.quality).first()
            if not stream:
                self.error_signal.emit(f'Не удалось найти видео с качеством {self.quality}')
                return

            logging.info(f'Начало загрузки видео: {yt.title}')
            stream.download(self.path)
            logging.info(f'Загрузка завершена: {yt.title}')
            self.finished_signal.emit()
        except Exception as e:
            self.error_signal.emit(f'Произошла ошибка: {e}')

    def on_progress(self, stream, chunk, bytes_remaining):
        total_size = stream.filesize
        bytes_downloaded = total_size - bytes_remaining
        percentage = (bytes_downloaded / total_size) * 100
        self.progress_signal.emit(int(percentage))

        speed = bytes_downloaded / (self.elapsed_time() or 1)  # bytes per second
        speed_str = f"{speed / (1024 * 1024):.2f} MB/s"
        self.speed_signal.emit(speed_str)

        time_remaining = bytes_remaining / speed if speed > 0 else 0
        time_str = f"{time_remaining / 60:.2f} мин"
        self.time_signal.emit(time_str)

    def elapsed_time(self):
        return (datetime.datetime.now() - self.start_time).total_seconds()

    def start(self):
        self.start_time = datetime.datetime.now()
        super().start()

class YouTubeDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('YouTube Downloader')
        self.setGeometry(100, 100, 800, 600)

        layout = QVBoxLayout()

        # URL input
        self.url_input = QLineEdit(self)
        self.url_input.setPlaceholderText('Введите URL видео с YouTube')
        self.url_input.textChanged.connect(self.update_preview)
        layout.addWidget(self.url_input)

        # Preview
        self.preview_label = QLabel(self)
        self.preview_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        layout.addWidget(self.preview_label)

        # Quality selection
        self.quality_combo = QComboBox(self)
        self.quality_combo.addItems(['Выберите качество', '1080p', '720p', '480p', '360p', '240p', '144p'])
        layout.addWidget(self.quality_combo)

        # Path selection
        self.path_button = QPushButton('Выбрать путь сохранения', self)
        self.path_button.clicked.connect(self.select_path)
        layout.addWidget(self.path_button)

        self.path_label = QLabel(self)
        self.path_label.setText('Путь для сохранения: ')
        layout.addWidget(self.path_label)

        # Download button
        self.download_button = QPushButton('Начать загрузку', self)
        self.download_button.clicked.connect(self.start_download)
        layout.addWidget(self.download_button)

        # Progress bar
        self.progress_bar = QProgressBar(self)
        layout.addWidget(self.progress_bar)

        # Speed and time labels
        self.speed_label = QLabel(self)
        self.speed_label.setText('Скорость: ')
        layout.addWidget(self.speed_label)

        self.time_label = QLabel(self)
        self.time_label.setText('Оставшееся время: ')
        layout.addWidget(self.time_label)

        # Log output
        self.log_output = QTextEdit(self)
        self.log_output.setReadOnly(True)
        layout.addWidget(self.log_output)

        self.setLayout(layout)

        # Apply CSS
        self.setStyleSheet("""
            QWidget {
                font-family: 'Arial', sans-serif;
                font-size: 14px;
                background-color: #f0f0f0;
            }
            QLineEdit, QComboBox {
                padding: 10px;
                margin: 10px;
                border: 2px solid #ccc;
                border-radius: 5px;
                background-color: #fff;
                color: #333;
            }
            QLineEdit:focus, QComboBox:focus {
                border-color: #e74c3c;
            }
            QPushButton {
                padding: 10px 20px;
                margin: 10px;
                border: none;
                border-radius: 5px;
                background-color: #e74c3c;
                color: #fff;
                font-weight: bold;
                text-transform: uppercase;
                letter-spacing: 1px;
            }
            QPushButton:hover {
                background-color: #c0392b;
            }
            QLabel {
                padding: 10px;
                color: #333;
            }
            QLabel#preview_label {
                border: 2px solid #ccc;
                border-radius: 5px;
                background-color: #fff;
            }
            QProgressBar {
                border: 2px solid #ccc;
                border-radius: 5px;
                text-align: center;
                background-color: #fff;
            }
            QProgressBar::chunk {
                background-color: #e74c3c;
                border-radius: 5px;
            }
            QTextEdit {
                border: 2px solid #ccc;
                border-radius: 5px;
                background-color: #fff;
                color: #333;
                padding: 10px;
            }
        """)

    def update_preview(self):
        url = self.url_input.text()
        if url:
            if hasattr(self, 'preview_thread') and self.preview_thread.isRunning():
                self.preview_thread.terminate()
            self.preview_thread = PreviewThread(url)
            self.preview_thread.preview_signal.connect(self.set_preview)
            self.preview_thread.error_signal.connect(self.set_error_preview)
            self.preview_thread.start()
        else:
            self.preview_label.setText('')

    def set_preview(self, pixmap):
        self.preview_label.setPixmap(pixmap.scaled(320, 240, Qt.AspectRatioMode.KeepAspectRatio))

    def set_error_preview(self, error_message):
        self.preview_label.setText(error_message)

    def select_path(self):
        options = QFileDialog.Option.ShowDirsOnly
        try:
            path = QFileDialog.getExistingDirectory(self, "Выберите папку для сохранения", options=options)
            if path:
                self.path_label.setText(f'Путь для сохранения: {path}')
        except Exception as e:
            self.log_output.append(f'Ошибка при выборе пути: {e}')
            QMessageBox.warning(self, 'Ошибка', f'Произошла ошибка при выборе пути: {e}')

    def start_download(self):
        url = self.url_input.text()
        quality = self.quality_combo.currentText()
        path = self.path_label.text().replace('Путь для сохранения: ', '')

        if not url or quality == 'Выберите качество' or not path:
            QMessageBox.warning(self, 'Ошибка', 'Пожалуйста, заполните все поля')
            return

        try:
            yt = YouTube(url)
            available_qualities = [stream.resolution for stream in yt.streams.filter(progressive=True)]
            self.log_output.append(f'Доступные качества: {available_qualities}')

            if quality not in available_qualities:
                QMessageBox.warning(self, 'Ошибка', f'Выбранное качество {quality} недоступно для этого видео')
                return

            self.download_thread = DownloadThread(url, quality, path)
            self.download_thread.progress_signal.connect(self.update_progress)
            self.download_thread.speed_signal.connect(self.update_speed)
            self.download_thread.time_signal.connect(self.update_time)
            self.download_thread.error_signal.connect(self.handle_download_error)
            self.download_thread.finished_signal.connect(self.download_finished)
            self.download_thread.start()

            # Создаем диалог прогресса
            self.progress_dialog = QProgressDialog("Загрузка видео...", "Отмена", 0, 100, self)
            self.progress_dialog.setWindowModality(Qt.WindowModality.WindowModal)
            self.progress_dialog.setAutoClose(False)
            self.progress_dialog.setAutoReset(False)
            self.progress_dialog.canceled.connect(self.cancel_download)
            self.progress_dialog.setValue(0)
        except Exception as e:
            self.log_output.append(f'Ошибка при загрузке видео: {e}')
            QMessageBox.warning(self, 'Ошибка', f'Произошла ошибка: {e}')

    def update_progress(self, value):
        self.progress_bar.setValue(value)
        if hasattr(self, 'progress_dialog'):
            self.progress_dialog.setValue(value)

    def update_speed(self, speed):
        self.speed_label.setText(f'Скорость: {speed}')

    def update_time(self, time):
        self.time_label.setText(f'Оставшееся время: {time}')

    def handle_download_error(self, error_message):
        self.log_output.append(error_message)
        QMessageBox.warning(self, 'Ошибка', error_message)

    def download_finished(self):
        self.log_output.append('Видео успешно скачано!')
        QMessageBox.information(self, 'Успех', 'Видео успешно скачано!')
        if hasattr(self, 'progress_dialog'):
            self.progress_dialog.reset()

    def cancel_download(self):
        if hasattr(self, 'download_thread') and self.download_thread.isRunning():
            self.download_thread.terminate()
            self.log_output.append('Загрузка отменена пользователем.')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = YouTubeDownloader()
    ex.show()
    sys.exit(app.exec()) - QWindowsWindow::setGeometry: Unable to set geometry 1366x741+0+23 (frame: 1382x780-8-8) on QWidgetWindow/"YouTubeDownloaderClassWindow" on "\\.\DISPLAY1". Resulting geometry: 1366x715+0+23 (frame: 1382x754-8-8) margins: 8, 31, 8, 8 minimum size: 362x741 MINMAXINFO(maxSize=POINT(x=0, y=0), maxpos=POINT(x=0, y=0), maxtrack=POINT(x=0, y=0), mintrack=POINT(x=378, y=780)))
Так же окно с программой все еще не отвечает
---------------------------
Ошибка
---------------------------
Произошла ошибка: HTTP Error 403: Forbidden
---------------------------
OK   
---------------------------