Skip to content

Commit

Permalink
media: Add typing/docstring cleanup for high level classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
benmoran56 committed May 10, 2024
1 parent 9553290 commit 5a52a79
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 105 deletions.
39 changes: 22 additions & 17 deletions pyglet/media/__init__.py
Expand Up @@ -40,6 +40,10 @@
The player provides a :py:meth:`Player.delete` method that can be used to
release resources immediately.
"""
from __future__ import annotations

from typing import TYPE_CHECKING, BinaryIO

from .drivers import get_audio_driver
from .player import Player, PlayerGroup
from .codecs import registry as _codec_registry
Expand All @@ -48,36 +52,37 @@

from . import synthesis

if TYPE_CHECKING:
from .codecs import MediaDecoder

__all__ = 'load', 'get_audio_driver', 'Player', 'PlayerGroup', 'SourceGroup', 'StaticSource', 'StreamingSource'


def load(filename, file=None, streaming=True, decoder=None):
"""Load a Source from a file.
def load(filename: str, file: BinaryIO | None = None,
streaming: bool = True, decoder: MediaDecoder | None = None) -> Source | StreamingSource:
"""Load a Source from disk, or an opened file.
All decoders that are registered for the filename extension are tried.
If none succeed, the exception from the first decoder is raised.
You can also specifically pass a decoder to use.
:Parameters:
`filename` : str
Used to guess the media format, and to load the file if `file` is
unspecified.
`file` : file-like object or None
Source of media data in any supported format.
`streaming` : bool
If `False`, a :class:`StaticSource` will be returned; otherwise
You can also specifically pass a decoder instance to use.
Args:
filename:
Used to guess the media format, and to load the file if ``file``
is unspecified.
file:
An optional file-like object containing the source data.
streaming:
If ``False``, a :class:`StaticSource` will be returned; otherwise
(default) a :class:`~pyglet.media.StreamingSource` is created.
`decoder` : MediaDecoder or None
decoder:
A specific decoder you wish to use, rather than relying on
automatic detection. If specified, no other decoders are tried.
:rtype: StreamingSource or Source
"""
if decoder:
return decoder.decode(filename, file, streaming=streaming)
else:
return _codec_registry.decode(filename, file, streaming=streaming)

return _codec_registry.decode(filename, file, streaming=streaming)


_add_default_codecs()
101 changes: 34 additions & 67 deletions pyglet/media/player.py
@@ -1,17 +1,23 @@
"""High-level sound and video player."""
from __future__ import annotations

from collections import deque
import time
from typing import Iterable, Optional, Union
from collections import deque

from typing import TYPE_CHECKING, Iterable, Generator

import pyglet

from pyglet.gl import GL_TEXTURE_2D
from pyglet.media import buffered_logger as bl
from pyglet.media.drivers import get_audio_driver
from pyglet.media.codecs.base import PreciseStreamingSource, Source, SourceGroup

_debug = pyglet.options['debug_media']

if TYPE_CHECKING:
from pyglet.image import Texture


class PlaybackTimer:
"""Playback Timer.
Expand Down Expand Up @@ -131,15 +137,11 @@ def __del__(self) -> None:
"""Release the Player resources."""
self.delete()

def queue(self, source: Union[Source, Iterable[Source]]) -> None:
"""
Queue the source on this player.
def queue(self, source: Source | Iterable[Source]) -> None:
"""Queue the source on this player.
If the player has no source, the player will start to play immediately
or pause depending on its :attr:`.playing` attribute.
Args:
source (Source or Iterable[Source]): The source to queue.
"""
if isinstance(source, (Source, SourceGroup)):
source = _one_item_playlist(source)
Expand All @@ -156,7 +158,7 @@ def queue(self, source: Union[Source, Iterable[Source]]) -> None:

self._set_playing(self._playing)

def _set_source(self, new_source: Optional[Source]) -> None:
def _set_source(self, new_source: Source | None) -> None:
if new_source is None:
self._source = None
else:
Expand Down Expand Up @@ -212,8 +214,7 @@ def _set_playing(self, playing: bool) -> None:

@property
def playing(self) -> bool:
"""
bool: Read-only. Determine if the player state is playing.
"""The current playing state.
The *playing* property is irrespective of whether or not there is
actually a source to play. If *playing* is ``True`` and a source is
Expand Down Expand Up @@ -307,14 +308,10 @@ def next_source(self) -> None:
self.dispatch_event('on_player_next_source')

def seek(self, timestamp: float) -> None:
"""
Seek for playback to the indicated timestamp on the current source.
"""Seek for playback to the indicated timestamp on the current source.
Timestamp is expressed in seconds. If the timestamp is outside the
duration of the source, it will be clamped to the end.
Args:
timestamp (float): The time where to seek in the source.
"""
playing = self._playing
if playing:
Expand Down Expand Up @@ -365,14 +362,13 @@ def _create_audio_player(self) -> None:
setattr(self, attr, value)

@property
def source(self) -> Optional[Source]:
"""Source: Read-only. The current :class:`Source`, or ``None``."""
def source(self) -> Source | None:
"""Read-only. The current :class:`Source`, or ``None``."""
return self._source

@property
def time(self) -> float:
"""
float: Read-only. Current playback time of the current source.
"""Read-only. Current playback time of the current source.
The playback time is a float expressed in seconds, with 0.0 being the
beginning of the media. The playback time returned represents the
Expand All @@ -391,47 +387,30 @@ def _create_texture(self) -> None:
return self._texture

@property
def texture(self) -> pyglet.image.Texture:
"""
:class:`pyglet.image.Texture`: Get the texture for the current video frame.
def texture(self) -> Texture | None:
"""Get the texture for the current video frame, if any.
You should call this method every time you display a frame of video,
as multiple textures might be used. The return value will be None if
there is no video in the current source.
You should query this property every time you display a frame of video,
as multiple textures might be used. This property will be ``None`` if
the current Source does not contain video.
"""
return self._texture

def get_texture(self) -> pyglet.image.Texture:
"""
Get the texture for the current video frame.
You should call this method every time you display a frame of video,
as multiple textures might be used. The return value will be None if
there is no video in the current source.
Returns:
:class:`pyglet.image.Texture`
.. deprecated:: 1.4
Use :attr:`~texture` instead
"""
return self.texture

def seek_next_frame(self) -> None:
"""Step forwards one video frame in the current source."""
time = self.source.get_next_video_timestamp()
if time is None:
return
self.seek(time)

def update_texture(self, dt: float = None) -> None:
def update_texture(self, dt: float | None = None) -> None:
"""Manually update the texture from the current source.
This happens automatically, so you shouldn't need to call this method.
Args:
dt (float): The time elapsed since the last call to
``update_texture``.
dt:
The time elapsed since the last call to ``update_texture``.
"""
# self.pr.disable()
# if dt > 0.05:
Expand Down Expand Up @@ -495,7 +474,7 @@ def update_texture(self, dt: float = None) -> None:
pyglet.clock.schedule_once(self.update_texture, delay)
# self.pr.enable()

def _video_finished(self, _dt) -> None:
def _video_finished(self, _dt: float) -> None:
if self._audio_player is None:
self.dispatch_event("on_eos")

Expand Down Expand Up @@ -573,10 +552,7 @@ def _video_finished(self, _dt) -> None:
# Events

def on_player_eos(self):
"""The player ran out of sources. The playlist is empty.
:event:
"""
"""The player ran out of sources. The playlist is empty."""
if _debug:
print('Player.on_player_eos')

Expand All @@ -588,8 +564,6 @@ def on_eos(self):
If :attr:`.loop` attribute is set to ``True``, the current source
will start to play again until :meth:`next_source` is called or
:attr:`.loop` is set to ``False``.
:event:
"""
if _debug:
print('Player.on_eos')
Expand All @@ -615,27 +589,24 @@ def on_player_next_source(self):
This is a useful event for adjusting the window size to the new
source :class:`VideoFormat` for example.
:event:
"""
pass

def on_driver_reset(self):
"""The audio driver has been reset, by default this will kill the current audio player and create a new one,
and requeue the buffers. Any buffers that may have been queued in a player will be resubmitted. It will
continue from the last buffers submitted, not played and may cause sync issues if using video.
"""The audio driver has been reset.
:event:
By default this will kill the current audio player, create a new one,
and requeue the buffers. Any buffers that may have been queued in a
player will be resubmitted. It will continue from the last buffers
submitted, not played, and may cause sync issues if using video.
"""
if self._audio_player is None:
return

self._audio_player.on_driver_reset()

# Voice has been changed, will need to reset all options on the voice.
for attr in ('volume', 'min_distance', 'max_distance', 'position',
'pitch', 'cone_orientation', 'cone_inner_angle',
'cone_outer_angle', 'cone_outer_gain'):
for attr in ('volume', 'min_distance', 'max_distance', 'position', 'pitch',
'cone_orientation', 'cone_inner_angle', 'cone_outer_angle', 'cone_outer_gain'):
value = getattr(self, attr)
setattr(self, attr, value)

Expand All @@ -649,7 +620,7 @@ def on_driver_reset(self):
Player.register_event_type('on_driver_reset')


def _one_item_playlist(source):
def _one_item_playlist(source: Source) -> Generator:
yield source


Expand All @@ -659,10 +630,6 @@ class PlayerGroup:
Create a player group for the given list of players.
All players in the group must currently not belong to any other group.
Args:
players (Iterable[Player]): Iterable of :class:`.Player` s in this
group.
"""

def __init__(self, players: Iterable[Player]) -> None:
Expand Down

0 comments on commit 5a52a79

Please sign in to comment.