Skip to content

Commit 75ebd21

Browse files
committed
Chore: A bit of cleanup and restructuring
1 parent 7b17aea commit 75ebd21

File tree

3 files changed

+133
-59
lines changed

3 files changed

+133
-59
lines changed

music_assistant_models/player_queue.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from dataclasses import dataclass, field
77
from typing import Any, Self
88

9-
from mashumaro import DataClassDictMixin
9+
from mashumaro import DataClassDictMixin, field_options, pass_through
1010

1111
from .enums import PlayerState, RepeatMode
12-
from .media_items import MediaItemType
12+
from .media_items import MediaItemType, media_from_dict
1313
from .queue_item import QueueItem
1414

1515

@@ -31,55 +31,68 @@ class PlayerQueue(DataClassDictMixin):
3131
display_name: str
3232
available: bool
3333
items: int
34-
3534
shuffle_enabled: bool = False
3635
repeat_mode: RepeatMode = RepeatMode.OFF
3736
dont_stop_the_music_enabled: bool = False
37+
3838
# current_index: index that is active (e.g. being played) by the player
3939
current_index: int | None = None
4040
# index_in_buffer: index that has been preloaded/buffered by the player
4141
index_in_buffer: int | None = None
42+
4243
elapsed_time: float = 0
4344
elapsed_time_last_updated: float = field(default_factory=time.time)
4445
state: PlayerState = PlayerState.IDLE
4546
current_item: QueueItem | None = None
4647
next_item: QueueItem | None = None
4748
radio_source: list[MediaItemType] = field(default_factory=list)
48-
enqueued_media_items: list[MediaItemType] = field(default_factory=list)
49+
4950
flow_mode: bool = False
5051
resume_pos: int = 0
51-
flow_mode_stream_log: list[PlayLogEntry] = field(default_factory=list)
52-
next_track_enqueued: str | None = None
52+
53+
#############################################################################
54+
# the fields below will only be used server-side and not sent to the client #
55+
#############################################################################
56+
57+
enqueued_media_items: list[MediaItemType] = field(
58+
default_factory=list,
59+
compare=False,
60+
metadata=field_options(serialize="omit", deserialize=pass_through),
61+
repr=False,
62+
)
63+
flow_mode_stream_log: list[PlayLogEntry] = field(
64+
default_factory=list,
65+
compare=False,
66+
metadata=field_options(serialize="omit", deserialize=pass_through),
67+
repr=False,
68+
)
69+
next_track_enqueued: str | None = field(
70+
default=None,
71+
compare=False,
72+
metadata=field_options(serialize="omit", deserialize=pass_through),
73+
repr=False,
74+
)
5375

5476
@property
5577
def corrected_elapsed_time(self) -> float:
5678
"""Return the corrected/realtime elapsed time."""
5779
return self.elapsed_time + (time.time() - self.elapsed_time_last_updated)
5880

59-
def __post_serialize__(self, d: dict[Any, Any]) -> dict[Any, Any]:
60-
"""Execute action(s) on serialization."""
61-
d.pop("flow_mode_stream_log", None)
62-
d.pop("next_track_enqueued", None)
63-
return d
64-
6581
def to_cache(self) -> dict[str, Any]:
6682
"""Return the dict that is suitable for storing into the cache db."""
6783
d = self.to_dict()
84+
d.pop("flow_mode", None)
6885
d.pop("current_item", None)
6986
d.pop("next_item", None)
7087
d.pop("index_in_buffer", None)
71-
d.pop("flow_mode", None)
72-
d.pop("next_track_enqueued", None)
73-
d.pop("flow_mode_stream_log", None)
88+
# enqueued_media_items needs to survive a restart
89+
# otherwise 'dont stop the music' will not work
90+
d["enqueued_media_items"] = [x.to_dict() for x in self.enqueued_media_items]
7491
return d
7592

7693
@classmethod
7794
def from_cache(cls, d: dict[Any, Any]) -> Self:
7895
"""Restore a PlayerQueue from a cache dict."""
79-
d.pop("current_item", None)
80-
d.pop("next_item", None)
81-
d.pop("index_in_buffer", None)
82-
d.pop("flow_mode", None)
83-
d.pop("next_track_enqueued", None)
84-
d.pop("flow_mode_stream_log", None)
96+
if "enqueued_media_items" in d:
97+
d["enqueued_media_items"] = [media_from_dict(x) for x in d["enqueued_media_items"]]
8598
return cls.from_dict(d)

music_assistant_models/queue_item.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,6 @@ def __post_init__(self) -> None:
3434
if not self.name:
3535
self.name = self.uri
3636

37-
def __post_serialize__(self, d: dict[Any, Any]) -> dict[Any, Any]:
38-
"""Execute action(s) on serialization."""
39-
# Exclude internal streamdetails fields from dict
40-
if streamdetails := d.get("streamdetails"):
41-
streamdetails.pop("data", None)
42-
streamdetails.pop("direct", None)
43-
streamdetails.pop("expires", None)
44-
streamdetails.pop("path", None)
45-
streamdetails.pop("decryption_key", None)
46-
return d
47-
4837
@property
4938
def uri(self) -> str:
5039
"""Return uri for this QueueItem (for logging purposes)."""

music_assistant_models/streamdetails.py

Lines changed: 99 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dataclasses import dataclass, field
66
from typing import Any
77

8-
from mashumaro import DataClassDictMixin
8+
from mashumaro import DataClassDictMixin, field_options, pass_through
99

1010
from .dsp import DSPDetails
1111
from .enums import MediaType, StreamType, VolumeNormalizationMode
@@ -31,72 +31,144 @@ class StreamDetails(DataClassDictMixin):
3131
# the streamdetails are only used to provide details about the content
3232
# that is going to be streamed.
3333

34-
# mandatory fields
34+
#############################################################################
35+
# mandatory fields #
36+
#############################################################################
3537
provider: str
3638
item_id: str
3739
audio_format: AudioFormat
3840
media_type: MediaType = MediaType.TRACK
3941
stream_type: StreamType = StreamType.CUSTOM
4042

41-
# optional fields
42-
43-
# path: url or (local accessible) path to the stream (if not custom stream)
44-
path: str | None = None
43+
#############################################################################
44+
# optional fields #
45+
#############################################################################
4546

4647
# duration of the item to stream, copied from media_item if omitted
4748
duration: int | None = None
49+
4850
# total size in bytes of the item, calculated at eof when omitted
4951
size: int | None = None
52+
53+
# stream_metadata: radio/live streams can optionally set/use this field
54+
# to set the metadata of any media during the stream
55+
stream_metadata: LivestreamMetadata | None = None
56+
57+
#############################################################################
58+
# the fields below will only be used server-side and not sent to the client #
59+
#############################################################################
60+
61+
# path: url or (local accessible) path to the stream (if not custom stream)
62+
# this field should be set by the provider when creating the streamdetails
63+
# unless the stream is a custom stream
64+
path: str | None = field(
65+
default=None,
66+
compare=False,
67+
metadata=field_options(serialize="omit", deserialize=pass_through),
68+
repr=False,
69+
)
5070
# data: provider specific data (not exposed externally)
5171
# this info is for example used to pass slong details to the get_audio_stream
52-
data: Any = None
72+
# this field may be set by the provider when creating the streamdetails
73+
data: Any = field(
74+
default=None,
75+
compare=False,
76+
metadata=field_options(serialize="omit", deserialize=pass_through),
77+
repr=False,
78+
)
79+
# extra_input_args: any additional input args to pass along to ffmpeg
80+
# this field may be set by the provider when creating the streamdetails
81+
extra_input_args: list[str] = field(
82+
default_factory=list,
83+
compare=False,
84+
metadata=field_options(serialize="omit", deserialize=pass_through),
85+
repr=False,
86+
)
87+
# decryption_key: decryption key for encrypted streams (used with StreamType.ENCRYPTED_HTTP)
88+
# this field may be set by the provider when creating the streamdetails
89+
decryption_key: str | None = field(
90+
default=None,
91+
compare=False,
92+
metadata=field_options(serialize="omit", deserialize=pass_through),
93+
repr=False,
94+
)
5395
# allow_seek: bool to indicate that the content can/may be seeked
5496
# If set to False, seeking will be completely disabled.
5597
# NOTE: this is automatically disabled for duration-less streams (e/g. radio)
56-
allow_seek: bool = True
98+
allow_seek: bool = field(
99+
default=False,
100+
compare=False,
101+
metadata=field_options(serialize="omit", deserialize=pass_through),
102+
repr=False,
103+
)
104+
57105
# can_seek: bool to indicate that the custom audio stream can be seeked
58106
# if set to False, and allow seek is set to True, the core logic will attempt
59107
# to seek in the incoming (bytes)stream, which is not a guarantee it will work.
60108
# If allow_seek is also set to False, seeking will be completely disabled.
61-
can_seek: bool = True
62-
# extra_input_args: any additional input args to pass along to ffmpeg
63-
extra_input_args: list[str] = field(default_factory=list)
64-
# decryption_key: decryption key for encrypted streams
65-
decryption_key: str | None = None
66-
# stream_metadata: radio/live streams can optionally set/use this field
67-
# to set the metadata of any media during the stream
68-
stream_metadata: LivestreamMetadata | None = None
69-
70-
# the fields below will be set/controlled by the streamcontroller
71-
seek_position: int = 0
72-
fade_in: bool = False
109+
can_seek: bool = field(
110+
default=False,
111+
compare=False,
112+
metadata=field_options(serialize="omit", deserialize=pass_through),
113+
repr=False,
114+
)
115+
116+
#############################################################################
117+
# the fields below will be set/controlled by the streamcontroller #
118+
#############################################################################
73119
loudness: float | None = None
74120
loudness_album: float | None = None
75121
prefer_album_loudness: bool = False
76122
volume_normalization_mode: VolumeNormalizationMode | None = None
77123
volume_normalization_gain_correct: float | None = None
78-
queue_id: str | None = None
79-
seconds_streamed: float | None = None
80124
target_loudness: float | None = None
81125
strip_silence_begin: bool = False
82126
strip_silence_end: bool = False
83-
stream_error: bool | None = None
127+
84128
# This contains the DSPDetails of all players in the group.
85129
# In case of single player playback, dict will contain only one entry.
86130
# The leader will have is_leader set to True.
87131
# (keep in mind that PlayerGroups have no (explicit) leader!)
88132
dsp: dict[str, DSPDetails] | None = None
89133

134+
# the fields below are managed by the queue/stream controller and may not be set by providers
135+
fade_in: bool = field(
136+
default=False,
137+
compare=False,
138+
metadata=field_options(serialize="omit", deserialize=pass_through),
139+
repr=False,
140+
)
141+
seek_position: int = field(
142+
default=0,
143+
compare=False,
144+
metadata=field_options(serialize="omit", deserialize=pass_through),
145+
repr=False,
146+
)
147+
queue_id: str | None = field(
148+
default=None,
149+
compare=False,
150+
metadata=field_options(serialize="omit", deserialize=pass_through),
151+
repr=False,
152+
)
153+
seconds_streamed: float | None = field(
154+
default=None,
155+
compare=False,
156+
metadata=field_options(serialize="omit", deserialize=pass_through),
157+
repr=False,
158+
)
159+
stream_error: bool | None = field(
160+
default=None,
161+
compare=False,
162+
metadata=field_options(serialize="omit", deserialize=pass_through),
163+
repr=False,
164+
)
165+
90166
def __str__(self) -> str:
91167
"""Return pretty printable string of object."""
92168
return self.uri
93169

94170
def __post_serialize__(self, d: dict[Any, Any]) -> dict[Any, Any]:
95171
"""Execute action(s) on serialization."""
96-
d.pop("queue_id", None)
97-
d.pop("seconds_streamed", None)
98-
d.pop("seek_position", None)
99-
d.pop("fade_in", None)
100172
# for backwards compatibility
101173
d["stream_title"] = self.stream_title
102174
return d

0 commit comments

Comments
 (0)