Compare commits

..

No commits in common. "9ff488d8074c3d39f1516df045fe6001520f77cc" and "ffc399290f313f0e016528fd1321900f1b991252" have entirely different histories.

5 changed files with 55 additions and 105 deletions

View file

@ -7,7 +7,7 @@ This enables your keyboard's standard media keys to control MPD, as well as more
The recommended way to install mpd-now-playable and its dependencies is with [pipx](https://pypa.github.io/pipx/). I'm currently unable to register a PyPI account, so to install you'll currently need to do something like this: The recommended way to install mpd-now-playable and its dependencies is with [pipx](https://pypa.github.io/pipx/). I'm currently unable to register a PyPI account, so to install you'll currently need to do something like this:
```shell ```shell
pipx install 'git+https://git.00dani.me/00dani/mpd-now-playable' pipx install 'https://git.00dani.me/00dani/mpd-now-playable/archive/main.tar.gz'
``` ```
However once PyPI opens registration again, this will work too, and it's the approach I definitely recommend: However once PyPI opens registration again, this will work too, and it's the approach I definitely recommend:

View file

@ -1,13 +1,10 @@
import asyncio import asyncio
from pathlib import Path
from uuid import UUID
from mpd.asyncio import MPDClient from mpd.asyncio import MPDClient
from mpd.base import CommandError from mpd.base import CommandError
from ..player import Player from ..player import Player
from ..song import PlaybackState, Song, SongListener from ..song import PlaybackState, Song, SongListener
from ..type_tools import convert_if_exists
from .artwork_cache import MpdArtworkCache from .artwork_cache import MpdArtworkCache
from .types import CurrentSongResponse, StatusResponse from .types import CurrentSongResponse, StatusResponse
@ -17,18 +14,10 @@ def mpd_current_to_song(
) -> Song: ) -> Song:
return Song( return Song(
state=PlaybackState(status["state"]), state=PlaybackState(status["state"]),
queue_index=int(current["pos"]), title=current["title"],
queue_length=int(status["playlistlength"]), artist=current["artist"],
file=Path(current["file"]), album=current["album"],
musicbrainz_trackid=convert_if_exists(current.get("musicbrainz_trackid"), UUID), album_artist=current["albumartist"],
title=current.get("title"),
artist=current.get("artist"),
album=current.get("album"),
album_artist=current.get("albumartist"),
composer=current.get("composer"),
genre=current.get("genre"),
track=convert_if_exists(current.get("track"), int),
disc=convert_if_exists(current.get("disc"), int),
duration=float(status["duration"]), duration=float(status["duration"]),
elapsed=float(status["elapsed"]), elapsed=float(status["elapsed"]),
art=art, art=art,

View file

@ -1,4 +1,4 @@
from typing import Literal, NotRequired, Protocol, TypedDict from typing import Protocol, TypedDict
class MpdStateHandler(Protocol): class MpdStateHandler(Protocol):
@ -9,74 +9,56 @@ class MpdStateHandler(Protocol):
... ...
BooleanFlag = Literal["0", "1"]
OneshotFlag = Literal[BooleanFlag, "oneshot"]
# This is not the complete status response from MPD, just the parts of it mpd-now-playable uses.
class StatusResponse(TypedDict): class StatusResponse(TypedDict):
state: Literal["play", "stop", "pause"] volume: str
repeat: str
# The total duration and elapsed playback of the current song, measured in seconds. Fractional seconds are allowed. random: str
duration: str single: str
elapsed: str consume: str
# The volume value ranges from 0-100. It may be omitted from
# the response entirely if MPD has no volume mixer configured.
volume: NotRequired[str]
# Various toggle-able music playback settings, which can be addressed and modified by Now Playing.
repeat: BooleanFlag
random: BooleanFlag
single: OneshotFlag
consume: OneshotFlag
# Partitions essentially let one MPD server act as multiple music players.
# For most folks, this will just be "default", but mpd-now-playable will
# eventually support addressing specific partitions. Eventually.
partition: str partition: str
playlist: str
# The total number of items in the play queue, which is called the "playlist" throughout the MPD protocol for legacy reasons.
playlistlength: str playlistlength: str
mixrampdb: str
# The format of decoded audio MPD is producing, expressed as a string in the form "samplerate:bits:channels". state: str
song: str
songid: str
time: str
elapsed: str
bitrate: str
duration: str
audio: str audio: str
nextsong: str
nextsongid: str
# All of these are metadata tags read from your music, and are strictly CurrentSongResponse = TypedDict(
# optional. mpd-now-playable will work better if your music is properly "CurrentSongResponse",
# tagged, since then it can pass more information on to Now Playing, but it {
# should work fine with completely untagged music too. "file": str,
class CurrentSongTags(TypedDict, total=False): "last-modified": str,
artist: str "format": str,
albumartist: str "artist": str,
artistsort: str "albumartist": str,
albumartistsort: str "artistsort": str,
title: str "albumartistsort": str,
album: str "title": str,
track: str "album": str,
date: str "track": str,
originaldate: str "date": str,
composer: str "originaldate": str,
disc: str "composer": str,
label: str "disc": str,
genre: str "label": str,
musicbrainz_albumid: str "musicbrainz_albumid": str,
musicbrainz_albumartistid: str "musicbrainz_albumartistid": str,
musicbrainz_releasetrackid: str "musicbrainz_releasetrackid": str,
musicbrainz_artistid: str "musicbrainz_artistid": str,
musicbrainz_trackid: str "musicbrainz_trackid": str,
"time": str,
"duration": str,
class CurrentSongResponse(CurrentSongTags): "pos": str,
# The name of the music file currently being played, as MPD understands "id": str,
# it. For locally stored music files, this'll just be a simple file path },
# relative to your music directory. )
file: str
# The index of the song in the play queue. Will change if you shuffle or
# otherwise reorder the playlist.
pos: str
ReadPictureResponse = TypedDict("ReadPictureResponse", {"binary": bytes}) ReadPictureResponse = TypedDict("ReadPictureResponse", {"binary": bytes})

View file

@ -1,7 +1,5 @@
from enum import StrEnum from enum import StrEnum
from pathlib import Path
from typing import Protocol from typing import Protocol
from uuid import UUID
from attrs import define, field from attrs import define, field
@ -15,18 +13,10 @@ class PlaybackState(StrEnum):
@define @define
class Song: class Song:
state: PlaybackState state: PlaybackState
queue_index: int title: str
queue_length: int artist: str
file: Path album: str
musicbrainz_trackid: UUID | None album_artist: str
title: str | None
artist: str | None
composer: str | None
album: str | None
album_artist: str | None
track: int | None
disc: int | None
genre: str | None
duration: float duration: float
elapsed: float elapsed: float
art: bytes | None = field(repr=lambda a: "<has art>" if a else "<no art>") art: bytes | None = field(repr=lambda a: "<has art>" if a else "<no art>")

View file

@ -1,11 +0,0 @@
from typing import Callable, TypeVar
__all__ = ("convert_if_exists",)
T = TypeVar("T")
def convert_if_exists(value: str | None, converter: Callable[[str], T]) -> T | None:
if value is None:
return None
return converter(value)