234 lines
8.0 KiB
Python
234 lines
8.0 KiB
Python
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}"
|