Support persistent track IDs (64-bit ints)
This commit is contained in:
parent
28da4da69f
commit
04a976f6f3
4 changed files with 46 additions and 4 deletions
|
@ -3,7 +3,7 @@ from os import environ
|
||||||
|
|
||||||
from corefoundationasyncio import CoreFoundationEventLoop
|
from corefoundationasyncio import CoreFoundationEventLoop
|
||||||
|
|
||||||
from .cocoa import CocoaNowPlaying
|
from .cocoa.now_playing import CocoaNowPlaying
|
||||||
from .mpd.listener import MpdStateListener
|
from .mpd.listener import MpdStateListener
|
||||||
|
|
||||||
|
|
||||||
|
|
0
src/mpd_now_playable/cocoa/__init__.py
Normal file
0
src/mpd_now_playable/cocoa/__init__.py
Normal file
|
@ -12,6 +12,7 @@ from MediaPlayer import (
|
||||||
MPMediaItemPropertyComposer,
|
MPMediaItemPropertyComposer,
|
||||||
MPMediaItemPropertyDiscNumber,
|
MPMediaItemPropertyDiscNumber,
|
||||||
MPMediaItemPropertyGenre,
|
MPMediaItemPropertyGenre,
|
||||||
|
MPMediaItemPropertyPersistentID,
|
||||||
MPMediaItemPropertyPlaybackDuration,
|
MPMediaItemPropertyPlaybackDuration,
|
||||||
MPMediaItemPropertyTitle,
|
MPMediaItemPropertyTitle,
|
||||||
MPMusicPlaybackState,
|
MPMusicPlaybackState,
|
||||||
|
@ -32,9 +33,10 @@ from MediaPlayer import (
|
||||||
MPRemoteCommandHandlerStatus,
|
MPRemoteCommandHandlerStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .async_tools import run_background_task
|
from ..async_tools import run_background_task
|
||||||
from .player import Player
|
from ..player import Player
|
||||||
from .song import PlaybackState, Song
|
from ..song import PlaybackState, Song
|
||||||
|
from .persistent_id import song_to_persistent_id
|
||||||
|
|
||||||
|
|
||||||
def logo_to_ns_image() -> NSImage:
|
def logo_to_ns_image() -> NSImage:
|
||||||
|
@ -81,6 +83,7 @@ def song_to_media_item(song: Song) -> NSMutableDictionary:
|
||||||
nowplaying_info[MPNowPlayingInfoPropertyExternalContentIdentifier] = song.file
|
nowplaying_info[MPNowPlayingInfoPropertyExternalContentIdentifier] = song.file
|
||||||
nowplaying_info[MPNowPlayingInfoPropertyPlaybackQueueCount] = song.queue_length
|
nowplaying_info[MPNowPlayingInfoPropertyPlaybackQueueCount] = song.queue_length
|
||||||
nowplaying_info[MPNowPlayingInfoPropertyPlaybackQueueIndex] = song.queue_index
|
nowplaying_info[MPNowPlayingInfoPropertyPlaybackQueueIndex] = song.queue_index
|
||||||
|
nowplaying_info[MPMediaItemPropertyPersistentID] = song_to_persistent_id(song)
|
||||||
|
|
||||||
nowplaying_info[MPMediaItemPropertyTitle] = song.title
|
nowplaying_info[MPMediaItemPropertyTitle] = song.title
|
||||||
nowplaying_info[MPMediaItemPropertyArtist] = song.artist
|
nowplaying_info[MPMediaItemPropertyArtist] = song.artist
|
39
src/mpd_now_playable/cocoa/persistent_id.py
Normal file
39
src/mpd_now_playable/cocoa/persistent_id.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
from hashlib import blake2b
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Final
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from ..song import Song
|
||||||
|
|
||||||
|
# The maximum size for a BLAKE2b "person" value is sixteen bytes, so we need to be concise.
|
||||||
|
HASH_PERSON_PREFIX: Final = b"mnp.mac."
|
||||||
|
TRACKID_HASH_PERSON: Final = HASH_PERSON_PREFIX + b"mb_tid"
|
||||||
|
FILE_HASH_PERSON: Final = HASH_PERSON_PREFIX + b"f"
|
||||||
|
|
||||||
|
PERSISTENT_ID_BITS: Final = 64
|
||||||
|
PERSISTENT_ID_BYTES: Final = PERSISTENT_ID_BITS // 8
|
||||||
|
|
||||||
|
|
||||||
|
def digest_trackid(trackid: UUID) -> bytes:
|
||||||
|
return blake2b(
|
||||||
|
trackid.bytes, digest_size=PERSISTENT_ID_BYTES, person=TRACKID_HASH_PERSON
|
||||||
|
).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def digest_file_uri(file: Path) -> bytes:
|
||||||
|
return blake2b(
|
||||||
|
bytes(file), digest_size=PERSISTENT_ID_BYTES, person=FILE_HASH_PERSON
|
||||||
|
).digest()
|
||||||
|
|
||||||
|
|
||||||
|
# The MPMediaItemPropertyPersistentID is only 64 bits, while a UUID is 128
|
||||||
|
# bits and not all tracks will even have their MusicBrainz track ID included.
|
||||||
|
# To work around this, we compute a BLAKE2 hash from the UUID, or failing
|
||||||
|
# that from the file URI. BLAKE2 can be customised to different digest sizes,
|
||||||
|
# making it perfect for this problem.
|
||||||
|
def song_to_persistent_id(song: Song) -> int:
|
||||||
|
if song.musicbrainz_trackid:
|
||||||
|
hashed_id = digest_trackid(song.musicbrainz_trackid)
|
||||||
|
else:
|
||||||
|
hashed_id = digest_file_uri(song.file)
|
||||||
|
return int.from_bytes(hashed_id)
|
Loading…
Reference in a new issue