Change Directory Layouts

This commit is contained in:
kokarare1212
2021-03-08 12:18:47 +09:00
parent ba29d64c2d
commit d8686312aa
16 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
from __future__ import annotations
from librespot.core.Session import Session
from librespot.player import PlayerConfiguration, StateWrapper
from librespot.player.metrics import PlaybackMetrics
from librespot.player.mixing import AudioSink
from librespot.player.playback.PlayerSession import PlayerSession
from librespot.player.state.DeviceStateHandler import DeviceStateHandler
from librespot.standard.Closeable import Closeable
import logging
import sched
import time
class Player(Closeable, PlayerSession.Listener, AudioSink.Listener):
VOLUME_MAX: int = 65536
_LOGGER: logging = logging.getLogger(__name__)
_scheduler: sched.scheduler = sched.scheduler(time.time)
_session: Session = None
_conf: PlayerConfiguration = None
_events: Player.EventsDispatcher = None
_sink: AudioSink = None
_metrics: dict[str, PlaybackMetrics] = dict()
_state: StateWrapper = None
_playerSession: PlayerSession = None
_releaseLineFuture = None
_deviceStateListener: DeviceStateHandler.Listener = None
def __init__(self, conf: PlayerConfiguration, session: Session):
self._conf = conf
self._session = session
self._events = Player.EventsDispatcher(conf)
self._sink = AudioSink(conf, self)
self._init_state()
def _init_state(self):
self._state = StateWrapper.StateWrapper(self._session, self, self._conf)
class Anonymous(DeviceStateHandler.Listener):
_player: Player = None
def __init__(self, player: Player):
self._player = player
def ready(self) -> None:
pass
def command(self, endpoint: DeviceStateHandler.Endpoint, data: DeviceStateHandler.CommandBody) -> None:
self._player._LOGGER.debug("Received command: {}".format(endpoint))
self._deviceStateListener = Anonymous(self)
self._state.add_listener(self._deviceStateListener)
def volume_up(self, steps: int = 1):
if self._state is None:
return
class EventsDispatcher:
def __init__(self, conf: PlayerConfiguration):
pass

View File

@@ -0,0 +1,79 @@
from __future__ import annotations
from librespot.player.codecs import AudioQuality
class PlayerConfiguration:
# Audio
preferred_quality: AudioQuality
enable_normalisation: bool
normalisation_pregain: float
autoplay_enabled: bool
crossfade_duration: int
preload_enabled: bool
# Volume
initial_volume: int
volume_steps: int
def __init__(self, preferred_quality: AudioQuality,
enable_normalisation: bool, normalisation_pregain: float,
autoplay_enabled: bool, crossfade_duration: int,
preload_enabled: bool, initial_volume: int,
volume_steps: int):
self.preferred_quality = preferred_quality
self.enable_normalisation = enable_normalisation
self.normalisation_pregain = normalisation_pregain
self.autoplay_enabled = autoplay_enabled
self.crossfade_duration = crossfade_duration
self.preload_enabled = preload_enabled
self.initial_volume = initial_volume
self.volume_steps = volume_steps
class Builder:
preferred_quality: AudioQuality = AudioQuality.AudioQuality.NORMAL
enable_normalisation: bool = True
normalisation_pregain: float = 3.0
autoplay_enabled: bool = True
crossfade_duration: int = 0
preload_enabled: bool = True
# Volume
initial_volume: int = 65536
volume_steps: int = 64
def __init__(self):
pass
def set_preferred_quality(
self, preferred_quality: AudioQuality) -> __class__:
self.preferred_quality = preferred_quality
return self
def set_enable_normalisation(self,
enable_normalisation: bool) -> __class__:
self.enable_normalisation = enable_normalisation
return self
def set_normalisation_pregain(
self, normalisation_pregain: float) -> __class__:
self.normalisation_pregain = normalisation_pregain
return self
def set_autoplay_enabled(self, autoplay_enabled: bool) -> __class__:
self.autoplay_enabled = autoplay_enabled
return self
def set_crossfade_duration(self, crossfade_duration: int) -> __class__:
self.crossfade_duration = crossfade_duration
return self
def set_preload_enabled(self, preload_enabled: bool) -> __class__:
self.preload_enabled = preload_enabled
return self
def build(self) -> PlayerConfiguration:
return PlayerConfiguration(
self.preferred_quality, self.enable_normalisation,
self.normalisation_pregain, self.autoplay_enabled,
self.crossfade_duration, self.preload_enabled,
self.initial_volume, self.volume_steps)

View File

@@ -0,0 +1,49 @@
from __future__ import annotations
from librespot.core import Session
from librespot.dealer import DealerClient
from librespot.player import Player, PlayerConfiguration
from librespot.player.state import DeviceStateHandler
from librespot.proto import Connect
from librespot.proto.Player import ContextPlayerOptions, PlayerState, Restrictions, Suppressions
class StateWrapper(DeviceStateHandler.Listener, DealerClient.MessageListener):
_state: PlayerState = None
_session: Session = None
_player: Player = None
_device: DeviceStateHandler = None
def __init__(self, session: Session, player: Player,
conf: PlayerConfiguration):
self._session = session
self._player = player
self._device = DeviceStateHandler(session, self, conf)
self._state = self._init_state()
self._device.add_listener(self)
self._session.dealer().add_message_listener(self, "spotify:user:attributes:update", "hm://playlist/", "hm://collection/collection/" + self._session.username() + "/json")
def _init_state(self) -> PlayerState:
return PlayerState(
playback_speed=1.0,
suppressions=Suppressions(),
context_restrictions=Restrictions(),
options=ContextPlayerOptions(
repeating_context=False,
shuffling_context=False,
repeating_track=False
),
position_as_of_timestamp=0,
position=0,
is_playing=False
)
def add_listener(self, listener: DeviceStateHandler.Listener):
self._device.add_listener(listener)
def ready(self) -> None:
self._device.update_state(Connect.PutStateReason.NEW_DEVICE, 0, self._state)
def on_message(self, uri: str, headers: dict[str, str],
payload: bytes):
pass

View File

@@ -0,0 +1,3 @@
from librespot.player.Player import Player
from librespot.player.PlayerConfiguration import PlayerConfiguration
from librespot.player.StateWrapper import StateWrapper

View File

@@ -0,0 +1,37 @@
from __future__ import annotations
from librespot.proto.Metadata import AudioFile
import enum
class AudioQuality(enum.Enum):
NORMAL = 0x00
HIGH = 0x01
VERY_HIGH = 0x02
@staticmethod
def get_quality(audio_format: AudioFile.Format) -> AudioQuality:
if audio_format == AudioFile.MP3_96 or \
audio_format == AudioFile.OGG_VORBIS_96 or \
audio_format == AudioFile.AAC_24_NORM:
return AudioQuality.NORMAL
elif audio_format == AudioFile.MP3_160 or \
audio_format == AudioFile.MP3_160_ENC or \
audio_format == AudioFile.OGG_VORBIS_160 or \
audio_format == AudioFile.AAC_24:
return AudioQuality.HIGH
elif audio_format == AudioFile.MP3_320 or \
audio_format == AudioFile.MP3_256 or \
audio_format == AudioFile.OGG_VORBIS_320 or \
audio_format == AudioFile.AAC_48:
return AudioQuality.VERY_HIGH
else:
raise RuntimeError("Unknown format: {}".format(format))
def get_matches(self, files: list[AudioFile]) -> list[AudioFile]:
file_list = []
for file in files:
if hasattr(file, "format") and AudioQuality.get_quality(
file.format) == self:
file_list.append(file)
return file_list

View File

@@ -0,0 +1,42 @@
from __future__ import annotations
import logging
from librespot.audio.format.AudioQualityPicker import AudioQualityPicker
from librespot.audio.format.SuperAudioFormat import SuperAudioFormat
from librespot.player.codecs.AudioQuality import AudioQuality
from librespot.proto import Metadata
class VorbisOnlyAudioQuality(AudioQualityPicker):
_LOGGER: logging = logging.getLogger(__name__)
preferred: AudioQuality
def __init__(self, preferred: AudioQuality):
self.preferred = preferred
@staticmethod
def get_vorbis_file(files: list[Metadata.AudioFile]):
for file in files:
if hasattr(file, "format") and SuperAudioFormat.get(
file.format) == SuperAudioFormat.VORBIS:
return file
return None
def get_file(self, files: list[Metadata.AudioFile]):
matches: list[Metadata.AudioFile] = self.preferred.get_matches(files)
vorbis: Metadata.AudioFile = VorbisOnlyAudioQuality.get_vorbis_file(
matches)
if vorbis is None:
vorbis: Metadata.AudioFile = VorbisOnlyAudioQuality.get_vorbis_file(
files)
if vorbis is not None:
self._LOGGER.warning(
"Using {} because preferred {} couldn't be found.".format(
vorbis.format, self.preferred))
else:
self._LOGGER.fatal(
"Couldn't find any Vorbis file, available: {}")
return vorbis

View File

@@ -0,0 +1,2 @@
from librespot.player.codecs.AudioQuality import AudioFile
from librespot.player.codecs.VorbisOnlyAudioQuality import VorbisOnlyAudioQuality

View File

@@ -0,0 +1,6 @@
from __future__ import annotations
import logging
class PlaybackMetrics:
_LOGGER = logging.getLogger(__name__)

View File

View File

@@ -0,0 +1,11 @@
from __future__ import annotations
from librespot.player import PlayerConfiguration
class AudioSink:
def __init__(self, conf: PlayerConfiguration, listener: AudioSink.Listener):
pass
class Listener:
def sink_error(self, ex: Exception):
pass

View File

@@ -0,0 +1 @@
from librespot.player.mixing.AudioSink import AudioSink

View File

@@ -0,0 +1,7 @@
from __future__ import annotations
from librespot.standard import Closeable
class PlayerSession(Closeable):
class Listener:
pass

View File

@@ -0,0 +1 @@
from librespot.player.playback.PlayerSession import PlayerSession

View File

@@ -0,0 +1,100 @@
from __future__ import annotations
from librespot.common import Utils
from librespot.core import Session
from librespot.player import PlayerConfiguration
from librespot.proto import Connect, Player
import base64
import concurrent.futures
import enum
import logging
import time
import typing
import urllib.parse
class DeviceStateHandler:
_LOGGER: logging = logging.getLogger(__name__)
_session: Session = None
_deviceInfo: Connect.DeviceInfo = None
_listeners: list[DeviceStateHandler.Listener] = list()
_putState: Connect.PutStateRequest = None
_putStateWorker: concurrent.futures.ThreadPoolExecutor = concurrent.futures.ThreadPoolExecutor()
_connectionId: str = None
def __init__(self, session: Session, player, conf: PlayerConfiguration):
self._session = session
self._deviceInfo = None
self._putState = Connect.PutStateRequest()
def _update_connection_id(self, newer: str) -> None:
newer = urllib.parse.unquote(newer, "UTF-8")
if self._connectionId is None or \
self._connectionId != newer:
self._connectionId = newer
self._LOGGER.debug("Updated Spotify-Connection-Id: {}".format(self._connectionId))
self._notify_ready()
def add_listener(self, listener: DeviceStateHandler.Listener):
self._listeners.append(listener)
def _notify_ready(self) -> None:
for listener in self._listeners:
listener.ready()
def update_state(self, reason: Connect.PutStateReason, player_time: int, state: Player.PlayerState):
if self._connectionId is None:
raise TypeError()
if player_time == -1:
pass
else:
pass
self._putState.put_state_reason = reason
self._putState.client_side_timestamp = int(time.time() * 1000)
self._putState.device.device_info = self._deviceInfo
self._putState.device.player_state = state
self._putStateWorker.submit(self._put_connect_state, self._putState)
def _put_connect_state(self, req: Connect.PutStateRequest):
self._session.api().put_connect_state(self._connectionId, req)
self._LOGGER.info("Put state. ts: {}, connId: {}, reason: {}".format(
req.client_side_timestamp, Utils.truncate_middle(self._connectionId, 10), req.put_state_reason
))
class Endpoint(enum.Enum):
Play: str = "play"
Pause: str = "pause"
Resume: str = "resume"
SeekTo: str = "seek_to"
SkipNext: str = "skip_next"
SkipPrev: str = "skip_prev"
class Listener:
def ready(self) -> None:
pass
def command(self, endpoint: DeviceStateHandler.Endpoint, data: DeviceStateHandler.CommandBody) -> None:
pass
def volume_changed(self) -> None:
pass
def not_active(self) -> None:
pass
class CommandBody:
_obj: typing.Any = None
_data: bytes = None
_value: str = None
def __init__(self, obj: typing.Any):
self._obj = obj
if obj.get("data") is not None:
self._data = base64.b64decode(obj.get("data"))
if obj.get("value") is not None:
self._value = obj.get("value")

View File

@@ -0,0 +1 @@
from librespot.player.state.DeviceStateHandler import DeviceStateHandler