from PySide6.QtWidgets import ( QWidget, QLabel, QVBoxLayout, QHBoxLayout, QPushButton, QSlider, QSizePolicy, ) from PySide6.QtCore import Qt, QSize, QTimer, Signal from PySide6.QtGui import QFont from controllers.media_controller import MediaController, TrackMetadata class MediaScreen(QWidget): source_changed = Signal(str) def __init__(self, media_controller: MediaController = None): super().__init__() self._controller = media_controller # Подключаемся на сигнал изменения метаданных if self._controller: self._controller.metadata_changed.connect(self._on_metadata_changed) root = QVBoxLayout(self) root.setContentsMargins(18, 16, 18, 16) root.setSpacing(14) header = QHBoxLayout() header.setContentsMargins(0, 0, 0, 0) header.setSpacing(12) info_col = QVBoxLayout() info_col.setContentsMargins(0, 0, 0, 0) info_col.setSpacing(6) self.source = QLabel("Источник: Bluetooth") self.source.setObjectName("MediaSource") self.source.setFont(QFont("", 14, 600)) self.title = QLabel("Название трека") self.title.setObjectName("MediaTitle") self.title.setFont(QFont("", 22, 700)) self.artist = QLabel("Исполнитель") self.artist.setObjectName("MediaArtist") self.artist.setFont(QFont("", 16, 600)) self.album = QLabel("Альбом") self.album.setObjectName("MediaAlbum") self.album.setFont(QFont("", 14, 600)) info_col.addWidget(self.source) info_col.addWidget(self.title) info_col.addWidget(self.artist) info_col.addWidget(self.album) info_col.addStretch(1) cover = QLabel("COVER") cover.setObjectName("MediaCover") cover.setAlignment(Qt.AlignCenter) cover.setFixedSize(QSize(240, 240)) header.addLayout(info_col, 1) header.addWidget(cover, 0, Qt.AlignRight | Qt.AlignTop) controls = QVBoxLayout() controls.setContentsMargins(0, 0, 0, 0) controls.setSpacing(12) time_row = QHBoxLayout() time_row.setContentsMargins(0, 0, 0, 0) time_row.setSpacing(10) self.time_pos = QLabel("0:00") self.time_pos.setObjectName("MediaTimePos") self.time_pos.setFont(QFont("", 12, 600)) self.time_total = QLabel("0:00") self.time_total.setObjectName("MediaTimeTotal") self.time_total.setFont(QFont("", 12, 600)) self.time_total.setAlignment(Qt.AlignRight | Qt.AlignVCenter) time_row.addWidget(self.time_pos) time_row.addStretch(1) time_row.addWidget(self.time_total) progress = QSlider(Qt.Horizontal) progress.setObjectName("MediaProgress") progress.setRange(0, 100) progress.setValue(35) self.progress = progress transport = QHBoxLayout() transport.setContentsMargins(0, 0, 0, 0) transport.setSpacing(16) btn_prev = QPushButton("⏮") btn_prev.setObjectName("MediaTransportBtn") btn_prev.setFixedSize(QSize(72, 72)) btn_prev.clicked.connect(self._prev) self.btn_play = QPushButton("▶") self.btn_play.setObjectName("MediaTransportBtnPrimary") self.btn_play.setFixedSize(QSize(96, 72)) self.btn_play.clicked.connect(self._toggle_play) btn_next = QPushButton("⏭") btn_next.setObjectName("MediaTransportBtn") btn_next.setFixedSize(QSize(72, 72)) btn_next.clicked.connect(self._next) transport.addStretch(1) transport.addWidget(btn_prev) transport.addWidget(self.btn_play) transport.addWidget(btn_next) transport.addStretch(1) volume_row = QHBoxLayout() volume_row.setContentsMargins(0, 0, 0, 0) volume_row.setSpacing(10) volume_lbl = QLabel("Громкость") volume_lbl.setObjectName("MediaVolumeLabel") volume_lbl.setFont(QFont("", 14, 600)) volume = QSlider(Qt.Horizontal) volume.setObjectName("MediaVolume") volume.setRange(0, 100) volume.setValue(55) volume.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) volume_row.addWidget(volume_lbl) volume_row.addWidget(volume, 1) controls.addLayout(time_row) controls.addWidget(progress) controls.addLayout(transport) controls.addLayout(volume_row) soft_keys = QHBoxLayout() soft_keys.setContentsMargins(0, 0, 0, 0) soft_keys.setSpacing(10) for label in ["SOURCE", "EQ", "FOLDER", "RANDOM", "REPEAT"]: btn = QPushButton(label) btn.setObjectName("MediaSoftBtn") btn.setMinimumHeight(52) soft_keys.addWidget(btn, 1) root.addLayout(header) root.addLayout(controls) root.addLayout(soft_keys) self._poll_timer = QTimer(self) self._poll_timer.timeout.connect(self._refresh_metadata) self._poll_timer.start(2000) self._refresh_metadata() def set_controller(self, controller: MediaController) -> None: """Установить контроллер.""" if self._controller: self._controller.metadata_changed.disconnect(self._on_metadata_changed) self._controller = controller if self._controller: self._controller.metadata_changed.connect(self._on_metadata_changed) self._refresh_metadata() def _on_metadata_changed(self, metadata: TrackMetadata) -> None: """Обновить UI при изменении метаданных.""" self._apply_metadata(metadata) def _apply_metadata(self, metadata: TrackMetadata) -> None: """Применить метаданные к UI.""" if metadata.title: self.title.setText(metadata.title) else: self.title.setText("Название трека") if metadata.artist: self.artist.setText(metadata.artist) else: self.artist.setText("Исполнитель") if metadata.album: self.album.setText(metadata.album) else: self.album.setText("Альбом") if metadata.source: text = f"Источник: {metadata.source}" self.source.setText(text) self.source_changed.emit(text) if metadata.duration is not None and metadata.duration > 0: self.progress.setRange(0, metadata.duration) self.time_total.setText(self._format_time(metadata.duration)) if metadata.position is not None: self.progress.setValue(metadata.position) self.time_pos.setText(self._format_time(metadata.position)) if metadata.status: self.btn_play.setText("⏸" if metadata.status == "playing" else "▶") def _toggle_play(self): """Переключить воспроизведение/пауза.""" if self._controller: self._controller.toggle_play() QTimer.singleShot(300, self._refresh_metadata) def _prev(self): """Предыдущий трек.""" if self._controller: self._controller.previous_track() QTimer.singleShot(300, self._refresh_metadata) def _next(self): """Следующий трек.""" if self._controller: self._controller.next_track() QTimer.singleShot(300, self._refresh_metadata) def _refresh_metadata(self): """Обновить метаданные трека.""" if not self._controller: return metadata = self._controller.get_metadata() self._apply_metadata(metadata) def _format_time(self, ms: int) -> str: """Форматировать время из миллисекунд.""" total_seconds = max(ms, 0) // 1000 minutes = total_seconds // 60 seconds = total_seconds % 60 return f"{minutes}:{seconds:02d}"