Enable seeking to specific points in the current song
This commit is contained in:
parent
04a976f6f3
commit
3372180a97
5 changed files with 62 additions and 20 deletions
|
@ -4,6 +4,7 @@ from pathlib import Path
|
||||||
from AppKit import NSCompositingOperationCopy, NSImage, NSMakeRect
|
from AppKit import NSCompositingOperationCopy, NSImage, NSMakeRect
|
||||||
from Foundation import CGSize, NSMutableDictionary
|
from Foundation import CGSize, NSMutableDictionary
|
||||||
from MediaPlayer import (
|
from MediaPlayer import (
|
||||||
|
MPChangePlaybackPositionCommandEvent,
|
||||||
MPMediaItemArtwork,
|
MPMediaItemArtwork,
|
||||||
MPMediaItemPropertyAlbumTitle,
|
MPMediaItemPropertyAlbumTitle,
|
||||||
MPMediaItemPropertyAlbumTrackNumber,
|
MPMediaItemPropertyAlbumTrackNumber,
|
||||||
|
@ -31,6 +32,7 @@ from MediaPlayer import (
|
||||||
MPRemoteCommandCenter,
|
MPRemoteCommandCenter,
|
||||||
MPRemoteCommandEvent,
|
MPRemoteCommandEvent,
|
||||||
MPRemoteCommandHandlerStatus,
|
MPRemoteCommandHandlerStatus,
|
||||||
|
MPRemoteCommandHandlerStatusSuccess,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..async_tools import run_background_task
|
from ..async_tools import run_background_task
|
||||||
|
@ -138,13 +140,17 @@ class CocoaNowPlaying:
|
||||||
cmd.removeTarget_(None)
|
cmd.removeTarget_(None)
|
||||||
cmd.addTargetWithHandler_(self._create_handler(handler))
|
cmd.addTargetWithHandler_(self._create_handler(handler))
|
||||||
|
|
||||||
|
seekCmd = self.cmd_center.changePlaybackPositionCommand()
|
||||||
|
seekCmd.setEnabled_(True)
|
||||||
|
seekCmd.removeTarget_(None)
|
||||||
|
seekCmd.addTargetWithHandler_(self._create_seek_handler(player.on_seek))
|
||||||
|
|
||||||
unsupported_cmds = (
|
unsupported_cmds = (
|
||||||
self.cmd_center.changePlaybackRateCommand(),
|
self.cmd_center.changePlaybackRateCommand(),
|
||||||
self.cmd_center.seekBackwardCommand(),
|
self.cmd_center.seekBackwardCommand(),
|
||||||
self.cmd_center.skipBackwardCommand(),
|
self.cmd_center.skipBackwardCommand(),
|
||||||
self.cmd_center.seekForwardCommand(),
|
self.cmd_center.seekForwardCommand(),
|
||||||
self.cmd_center.skipForwardCommand(),
|
self.cmd_center.skipForwardCommand(),
|
||||||
self.cmd_center.changePlaybackPositionCommand(),
|
|
||||||
)
|
)
|
||||||
for cmd in unsupported_cmds:
|
for cmd in unsupported_cmds:
|
||||||
cmd.setEnabled_(False)
|
cmd.setEnabled_(False)
|
||||||
|
@ -175,3 +181,14 @@ class CocoaNowPlaying:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
def _create_seek_handler(
|
||||||
|
self, player: Callable[[float], Coroutine[None, None, None]]
|
||||||
|
) -> Callable[[MPChangePlaybackPositionCommandEvent], MPRemoteCommandHandlerStatus]:
|
||||||
|
def handler(
|
||||||
|
event: MPChangePlaybackPositionCommandEvent,
|
||||||
|
) -> MPRemoteCommandHandlerStatus:
|
||||||
|
run_background_task(player(event.positionTime()))
|
||||||
|
return MPRemoteCommandHandlerStatusSuccess
|
||||||
|
|
||||||
|
return handler
|
||||||
|
|
|
@ -126,3 +126,6 @@ class MpdStateListener(Player):
|
||||||
|
|
||||||
async def on_prev(self) -> None:
|
async def on_prev(self) -> None:
|
||||||
await self.client.previous()
|
await self.client.previous()
|
||||||
|
|
||||||
|
async def on_seek(self, position: float) -> None:
|
||||||
|
await self.client.seekcur(position)
|
||||||
|
|
|
@ -21,3 +21,6 @@ class Player(Protocol):
|
||||||
|
|
||||||
async def on_prev(self) -> None:
|
async def on_prev(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
async def on_seek(self, position: float) -> None:
|
||||||
|
...
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Final, Literal
|
from typing import Final, Literal, override
|
||||||
|
|
||||||
from AppKit import NSImage
|
from AppKit import NSImage
|
||||||
from Foundation import CGSize, NSMutableDictionary
|
from Foundation import CGSize, NSMutableDictionary
|
||||||
|
@ -52,18 +52,34 @@ class MPNowPlayingInfoCenter:
|
||||||
def setNowPlayingInfo_(self, info: NSMutableDictionary) -> None: ...
|
def setNowPlayingInfo_(self, info: NSMutableDictionary) -> None: ...
|
||||||
def setPlaybackState_(self, state: MPMusicPlaybackState) -> None: ...
|
def setPlaybackState_(self, state: MPMusicPlaybackState) -> None: ...
|
||||||
|
|
||||||
MPRemoteCommandHandlerStatusSuccess: Literal[0] = 0
|
MPRemoteCommandHandlerStatusSuccess: Final = 0
|
||||||
MPRemoteCommandHandlerStatusCommandFailed: Literal[200] = 200
|
MPRemoteCommandHandlerStatusCommandFailed: Final = 200
|
||||||
MPRemoteCommandHandlerStatus = Literal[0, 200]
|
MPRemoteCommandHandlerStatus = Literal[0, 200]
|
||||||
|
|
||||||
class MPRemoteCommandEvent:
|
class MPRemoteCommandEvent:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class MPChangePlaybackPositionCommandEvent(MPRemoteCommandEvent):
|
||||||
|
def positionTime(self) -> float:
|
||||||
|
"""Return the requested playback position as a number of seconds (fractional seconds are allowed)."""
|
||||||
|
pass
|
||||||
|
|
||||||
class MPRemoteCommand:
|
class MPRemoteCommand:
|
||||||
def setEnabled_(self, enabled: bool) -> None: ...
|
def setEnabled_(self, enabled: bool) -> None: ...
|
||||||
def removeTarget_(self, target: object) -> None: ...
|
def removeTarget_(self, target: object) -> None: ...
|
||||||
def addTargetWithHandler_(
|
def addTargetWithHandler_(
|
||||||
self, handler: Callable[[MPRemoteCommandEvent], MPRemoteCommandHandlerStatus]
|
self, handler: Callable[[MPRemoteCommandEvent], MPRemoteCommandHandlerStatus]
|
||||||
|
) -> None:
|
||||||
|
"""Register a callback to handle the commands. Many remote commands don't carry useful information in the event object (play, pause, next track, etc.), so the callback does not necessarily need to care about the event argument."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MPChangePlaybackPositionCommand(MPRemoteCommand):
|
||||||
|
@override
|
||||||
|
def addTargetWithHandler_(
|
||||||
|
self,
|
||||||
|
handler: Callable[
|
||||||
|
[MPChangePlaybackPositionCommandEvent], MPRemoteCommandHandlerStatus
|
||||||
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
class MPRemoteCommandCenter:
|
class MPRemoteCommandCenter:
|
||||||
|
@ -80,4 +96,4 @@ class MPRemoteCommandCenter:
|
||||||
def skipBackwardCommand(self) -> MPRemoteCommand: ...
|
def skipBackwardCommand(self) -> MPRemoteCommand: ...
|
||||||
def seekForwardCommand(self) -> MPRemoteCommand: ...
|
def seekForwardCommand(self) -> MPRemoteCommand: ...
|
||||||
def skipForwardCommand(self) -> MPRemoteCommand: ...
|
def skipForwardCommand(self) -> MPRemoteCommand: ...
|
||||||
def changePlaybackPositionCommand(self) -> MPRemoteCommand: ...
|
def changePlaybackPositionCommand(self) -> MPChangePlaybackPositionCommand: ...
|
||||||
|
|
|
@ -11,13 +11,16 @@ class MPDClient(MPDClientBase):
|
||||||
async def connect(self, host: str, port: int = ...) -> None: ...
|
async def connect(self, host: str, port: int = ...) -> None: ...
|
||||||
async def password(self, password: str) -> None: ...
|
async def password(self, password: str) -> None: ...
|
||||||
def idle(self, subsystems: Sequence[str] = ...) -> AsyncIterator[Sequence[str]]: ...
|
def idle(self, subsystems: Sequence[str] = ...) -> AsyncIterator[Sequence[str]]: ...
|
||||||
|
|
||||||
async def status(self) -> types.StatusResponse: ...
|
async def status(self) -> types.StatusResponse: ...
|
||||||
async def currentsong(self) -> types.CurrentSongResponse: ...
|
async def currentsong(self) -> types.CurrentSongResponse: ...
|
||||||
async def readpicture(self, uri: str) -> types.ReadPictureResponse: ...
|
async def readpicture(self, uri: str) -> types.ReadPictureResponse: ...
|
||||||
|
|
||||||
async def play(self) -> None: ...
|
async def play(self) -> None: ...
|
||||||
async def pause(self, pause: Literal[1, 0, None] = None) -> None: ...
|
async def pause(self, pause: Literal[1, 0, None] = None) -> None:
|
||||||
|
"""Pause MPD or toggle its play/pause state. Pass pause=1 to unconditionally pause, pause=0 to unconditionally unpause, or pause=None to toggle."""
|
||||||
|
pass
|
||||||
async def stop(self) -> None: ...
|
async def stop(self) -> None: ...
|
||||||
async def next(self) -> None: ... # noqa: A003
|
async def next(self) -> None: ... # noqa: A003
|
||||||
async def previous(self) -> None: ...
|
async def previous(self) -> None: ...
|
||||||
|
async def seekcur(self, position: float) -> None:
|
||||||
|
"""Seek to a particular time in the currently playing song, measured in seconds. Fractional seconds are supported."""
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in a new issue