253 lines
7.8 KiB
Python
253 lines
7.8 KiB
Python
from __future__ import annotations
|
||
|
||
from abc import ABC, abstractmethod
|
||
from dataclasses import dataclass
|
||
from typing import Any
|
||
|
||
from PySide6.QtCore import QObject, Signal
|
||
|
||
|
||
@dataclass
|
||
class TrackMetadata:
|
||
"""Метаданные трека."""
|
||
title: str = ""
|
||
artist: str = ""
|
||
album: str = ""
|
||
source: str = ""
|
||
position: int = 0 # ms
|
||
duration: int = 0 # ms
|
||
status: str = "" # "playing", "paused", "stopped"
|
||
|
||
|
||
class MediaSourceController(ABC):
|
||
"""Базовый класс контроллера источника медиа."""
|
||
|
||
@property
|
||
@abstractmethod
|
||
def name(self) -> str:
|
||
"""Название источника."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def get_volume(self) -> int:
|
||
"""Получить текущую громкость (0-100)."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def set_volume(self, value: int) -> None:
|
||
"""Установить громкость (0-100)."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def get_metadata(self) -> TrackMetadata:
|
||
"""Получить метаданные текущего трека."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def play(self) -> None:
|
||
"""Воспроизведение."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def pause(self) -> None:
|
||
"""Пауза."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def toggle_play(self) -> None:
|
||
"""Переключить воспроизведение/пауза."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def next_track(self) -> None:
|
||
"""Следующий трек."""
|
||
pass
|
||
|
||
@abstractmethod
|
||
def previous_track(self) -> None:
|
||
"""Предыдущий трек."""
|
||
pass
|
||
|
||
|
||
class BluetoothController(MediaSourceController):
|
||
"""Контроллер для Bluetooth аудио."""
|
||
|
||
def __init__(self, bt_service: Any):
|
||
self._bt_service = bt_service
|
||
|
||
@property
|
||
def name(self) -> str:
|
||
return "Bluetooth"
|
||
|
||
def get_volume(self) -> int:
|
||
# TODO: получить громкость из Bluetooth сервиса
|
||
return 50
|
||
|
||
def set_volume(self, value: int) -> None:
|
||
# TODO: установить громкость в Bluetooth сервис
|
||
pass
|
||
|
||
def get_metadata(self) -> TrackMetadata:
|
||
if self._bt_service is None:
|
||
return TrackMetadata()
|
||
|
||
metadata = self._bt_service.get_player_metadata()
|
||
return TrackMetadata(
|
||
title=metadata.get("title", ""),
|
||
artist=metadata.get("artist", ""),
|
||
album=metadata.get("album", ""),
|
||
source=metadata.get("source", ""),
|
||
position=metadata.get("position", 0) or 0,
|
||
duration=metadata.get("duration", 0) or 0,
|
||
status=metadata.get("status", ""),
|
||
)
|
||
|
||
def play(self) -> None:
|
||
if self._bt_service:
|
||
self._bt_service.player_play()
|
||
|
||
def pause(self) -> None:
|
||
if self._bt_service:
|
||
self._bt_service.player_pause()
|
||
|
||
def toggle_play(self) -> None:
|
||
if self._bt_service:
|
||
self._bt_service.player_toggle()
|
||
|
||
def next_track(self) -> None:
|
||
if self._bt_service:
|
||
self._bt_service.player_next()
|
||
|
||
def previous_track(self) -> None:
|
||
if self._bt_service:
|
||
self._bt_service.player_previous()
|
||
|
||
|
||
class CarPlayController(MediaSourceController):
|
||
"""Контроллер для CarPlay (заглушка)."""
|
||
|
||
def __init__(self):
|
||
self._volume = 50
|
||
|
||
@property
|
||
def name(self) -> str:
|
||
return "CarPlay"
|
||
|
||
def get_volume(self) -> int:
|
||
return self._volume
|
||
|
||
def set_volume(self, value: int) -> None:
|
||
self._volume = max(0, min(100, value))
|
||
|
||
def get_metadata(self) -> TrackMetadata:
|
||
# Заглушка - нет данных
|
||
return TrackMetadata(
|
||
title="",
|
||
artist="",
|
||
album="",
|
||
source="CarPlay",
|
||
position=0,
|
||
duration=0,
|
||
status="stopped",
|
||
)
|
||
|
||
def play(self) -> None:
|
||
# TODO: реализовать через CarPlay API
|
||
pass
|
||
|
||
def pause(self) -> None:
|
||
# TODO: реализовать через CarPlay API
|
||
pass
|
||
|
||
def toggle_play(self) -> None:
|
||
# TODO: реализовать через CarPlay API
|
||
pass
|
||
|
||
def next_track(self) -> None:
|
||
# TODO: реализовать через CarPlay API
|
||
pass
|
||
|
||
def previous_track(self) -> None:
|
||
# TODO: реализовать через CarPlay API
|
||
pass
|
||
|
||
|
||
class MediaController(QObject):
|
||
"""
|
||
Главный контроллер медиа.
|
||
Выбирает активный контроллер в зависимости от режима.
|
||
"""
|
||
|
||
volume_changed = Signal(int)
|
||
metadata_changed = Signal(object) # TrackMetadata
|
||
|
||
def __init__(self, bt_service: Any = None, parent: QObject = None):
|
||
super().__init__(parent)
|
||
self._bt_service = bt_service
|
||
self._controllers: dict[str, MediaSourceController] = {}
|
||
self._current_mode: str = "bluetooth"
|
||
|
||
# Регистрируем контроллеры
|
||
self._register_controllers()
|
||
|
||
def _register_controllers(self) -> None:
|
||
"""Зарегистрировать все доступные контроллеры."""
|
||
self._controllers["bluetooth"] = BluetoothController(self._bt_service)
|
||
self._controllers["carplay"] = CarPlayController()
|
||
# В будущем можно добавить:
|
||
# self._controllers["aux"] = AuxController()
|
||
|
||
def set_mode(self, mode: str) -> None:
|
||
"""Установить активный режим."""
|
||
if mode in self._controllers:
|
||
# Если переключаемся с Bluetooth на CarPlay - ставим паузу
|
||
if self._current_mode == "bluetooth" and mode == "carplay":
|
||
self._controllers["bluetooth"].pause()
|
||
self._current_mode = mode
|
||
# Сигнал о смене режима для обновления UI
|
||
self.metadata_changed.emit(self.get_metadata())
|
||
|
||
def get_mode(self) -> str:
|
||
"""Получить текущий режим."""
|
||
return self._current_mode
|
||
|
||
@property
|
||
def _current_controller(self) -> MediaSourceController:
|
||
"""Получить активный контроллер."""
|
||
return self._controllers.get(self._current_mode, self._controllers["bluetooth"])
|
||
|
||
# === Делегирующие методы ===
|
||
|
||
def get_volume(self) -> int:
|
||
"""Получить текущую громкость."""
|
||
return self._current_controller.get_volume()
|
||
|
||
def set_volume(self, value: int) -> None:
|
||
"""Установить громкость."""
|
||
self._current_controller.set_volume(value)
|
||
self.volume_changed.emit(value)
|
||
|
||
def get_metadata(self) -> TrackMetadata:
|
||
"""Получить метаданные текущего трека."""
|
||
return self._current_controller.get_metadata()
|
||
|
||
def play(self) -> None:
|
||
"""Воспроизведение."""
|
||
self._current_controller.play()
|
||
|
||
def pause(self) -> None:
|
||
"""Пауза."""
|
||
self._current_controller.pause()
|
||
|
||
def toggle_play(self) -> None:
|
||
"""Переключить воспроизведение/пауза."""
|
||
self._current_controller.toggle_play()
|
||
|
||
def next_track(self) -> None:
|
||
"""Следующий трек."""
|
||
self._current_controller.next_track()
|
||
|
||
def previous_track(self) -> None:
|
||
"""Предыдущий трек."""
|
||
self._current_controller.previous_track()
|