python-linkplay 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
linkplay/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.1.2'
1
+ __version__ = '0.2.0'
linkplay/bridge.py CHANGED
@@ -22,6 +22,7 @@ from linkplay.consts import (
22
22
  )
23
23
  from linkplay.endpoint import LinkPlayEndpoint
24
24
  from linkplay.exceptions import LinkPlayInvalidDataException
25
+ from linkplay.manufacturers import MANUFACTURER_WIIM, get_info_from_project
25
26
  from linkplay.utils import fixup_player_properties
26
27
 
27
28
 
@@ -57,6 +58,22 @@ class LinkPlayDevice:
57
58
  """The name of the device."""
58
59
  return self.properties.get(DeviceAttribute.DEVICE_NAME, "")
59
60
 
61
+ @property
62
+ def manufacturer(self) -> str:
63
+ """The manufacturer of the device."""
64
+ manufacturer, _ = get_info_from_project(
65
+ self.properties.get(DeviceAttribute.PROJECT, "")
66
+ )
67
+ return manufacturer
68
+
69
+ @property
70
+ def model(self) -> str:
71
+ """The model of the device."""
72
+ _, model = get_info_from_project(
73
+ self.properties.get(DeviceAttribute.PROJECT, "")
74
+ )
75
+ return model
76
+
60
77
  @property
61
78
  def playmode_support(self) -> list[PlayingMode]:
62
79
  """Returns the player playmode support."""
@@ -69,13 +86,23 @@ class LinkPlayDevice:
69
86
  playing_modes.insert(0, PlayingMode.NETWORK) # always supported
70
87
  return playing_modes
71
88
 
89
+ @property
90
+ def mac(self) -> str | None:
91
+ """Returns the mac address."""
92
+ mac = self.properties.get(DeviceAttribute.ETH_MAC_ADDRESS)
93
+ if mac == "00:00:00:00:00:00" or mac is None:
94
+ mac = self.properties.get(DeviceAttribute.STA_MAC_ADDRESS)
95
+ if mac == "00:00:00:00:00:00" or mac is None:
96
+ mac = self.properties.get(DeviceAttribute.MAC_ADDRESS)
97
+ return mac
98
+
72
99
  @property
73
100
  def eth(self) -> str | None:
74
101
  """Returns the ethernet address."""
75
102
  eth = self.properties.get(DeviceAttribute.ETH2)
76
- if eth == "0.0.0.0" or eth is None:
103
+ if eth == "0.0.0.0" or eth == "" or eth is None:
77
104
  eth = self.properties.get(DeviceAttribute.ETH0)
78
- if eth == "0.0.0.0" or eth is None:
105
+ if eth == "0.0.0.0" or eth == "" or eth is None:
79
106
  eth = self.properties.get(DeviceAttribute.APCLI0)
80
107
  return eth
81
108
 
@@ -160,7 +187,47 @@ class LinkPlayPlayer:
160
187
 
161
188
  async def set_equalizer_mode(self, mode: EqualizerMode) -> None:
162
189
  """Set the equalizer mode."""
163
- await self.bridge.request(LinkPlayCommand.EQUALIZER_MODE.format(mode)) # type: ignore[str-format]
190
+ if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
191
+ # WiiM devices have a different equalizer mode handling
192
+ # and don't support the general equalizer mode command
193
+ if mode not in self.available_equalizer_modes:
194
+ raise ValueError(
195
+ f"Invalid equalizer mode {mode}. Allowed equalizer modes are {self.available_equalizer_modes}."
196
+ )
197
+
198
+ if mode == EqualizerMode.NONE:
199
+ await self.bridge.json_request(LinkPlayCommand.WIIM_EQUALIZER_OFF)
200
+ else:
201
+ await self.bridge.json_request(
202
+ LinkPlayCommand.WIIM_EQ_LOAD.format(mode)
203
+ )
204
+ # WiiM doesn't update the property after setting it
205
+ self.properties[PlayerAttribute.EQUALIZER_MODE] = mode
206
+ else:
207
+ await self._set_normal_equalizer_mode(mode)
208
+
209
+ async def _set_normal_equalizer_mode(self, mode: EqualizerMode) -> None:
210
+ """Set the equalizer mode."""
211
+ equalizer_mode_as_number = None
212
+ match mode:
213
+ case EqualizerMode.NONE:
214
+ equalizer_mode_as_number = "0"
215
+ case EqualizerMode.CLASSIC:
216
+ equalizer_mode_as_number = "1"
217
+ case EqualizerMode.POP:
218
+ equalizer_mode_as_number = "2"
219
+ case EqualizerMode.JAZZ:
220
+ equalizer_mode_as_number = "3"
221
+ case EqualizerMode.VOCAL:
222
+ equalizer_mode_as_number = "4"
223
+ if equalizer_mode_as_number is None:
224
+ raise ValueError(
225
+ f"Invalid equalizer mode {mode}. Allowed equalizer modes are {self.available_equalizer_modes}."
226
+ )
227
+
228
+ await self.bridge.request(
229
+ LinkPlayCommand.EQUALIZER_MODE.format(equalizer_mode_as_number)
230
+ )
164
231
 
165
232
  async def set_loop_mode(self, mode: LoopMode) -> None:
166
233
  """Set the loop mode."""
@@ -250,10 +317,53 @@ class LinkPlayPlayer:
250
317
  @property
251
318
  def equalizer_mode(self) -> EqualizerMode:
252
319
  """Returns the current equalizer mode."""
320
+ if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
321
+ return EqualizerMode(
322
+ self.properties.get(PlayerAttribute.EQUALIZER_MODE, EqualizerMode.NONE)
323
+ )
324
+
253
325
  return EqualizerMode(
254
326
  self.properties.get(PlayerAttribute.EQUALIZER_MODE, EqualizerMode.CLASSIC)
255
327
  )
256
328
 
329
+ @property
330
+ def available_equalizer_modes(self) -> list[EqualizerMode]:
331
+ """Returns the available equalizer modes."""
332
+ if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
333
+ return [
334
+ EqualizerMode.NONE,
335
+ EqualizerMode.FLAT,
336
+ EqualizerMode.ACOUSTIC,
337
+ EqualizerMode.BASS_BOOSTER,
338
+ EqualizerMode.BASS_REDUCER,
339
+ EqualizerMode.CLASSICAL,
340
+ EqualizerMode.DANCE,
341
+ EqualizerMode.DEEP,
342
+ EqualizerMode.ELECTRONIC,
343
+ EqualizerMode.HIP_HOP,
344
+ EqualizerMode.JAZZ,
345
+ EqualizerMode.LATIN,
346
+ EqualizerMode.LOUDNESS,
347
+ EqualizerMode.LOUNGE,
348
+ EqualizerMode.PIANO,
349
+ EqualizerMode.POP,
350
+ EqualizerMode.R_B,
351
+ EqualizerMode.ROCK,
352
+ EqualizerMode.SMALL_SPEAKERS,
353
+ EqualizerMode.SPOKEN_WORD,
354
+ EqualizerMode.TREBLE_BOOSTER,
355
+ EqualizerMode.TREBLE_REDUCER,
356
+ EqualizerMode.VOCAL_BOOSTER,
357
+ ]
358
+
359
+ return [
360
+ EqualizerMode.NONE,
361
+ EqualizerMode.CLASSIC,
362
+ EqualizerMode.POP,
363
+ EqualizerMode.JAZZ,
364
+ EqualizerMode.VOCAL,
365
+ ]
366
+
257
367
  @property
258
368
  def speaker_type(self) -> SpeakerType:
259
369
  """Returns the current speaker the player is playing on."""
linkplay/consts.py CHANGED
@@ -85,6 +85,8 @@ class LinkPlayCommand(StrEnum):
85
85
  STOP = "setPlayerCmd:stop"
86
86
  TOGGLE = "setPlayerCmd:onepause"
87
87
  EQUALIZER_MODE = "setPlayerCmd:equalizer:{}"
88
+ WIIM_EQUALIZER_ON = "EQOn"
89
+ WIIM_EQUALIZER_OFF = "EQOff"
88
90
  LOOP_MODE = "setPlayerCmd:loopmode:{}"
89
91
  SWITCH_MODE = "setPlayerCmd:switchmode:{}"
90
92
  M3U_PLAYLIST = "setPlayerCmd:m3u:play:{}"
@@ -97,6 +99,7 @@ class LinkPlayCommand(StrEnum):
97
99
  MULTIROOM_JOIN = "ConnectMasterAp:JoinGroupMaster:eth{}:wifi0.0.0.0"
98
100
  PLAY_PRESET = "MCUKeyShortClick:{}"
99
101
  TIMESYNC = "timeSync:{}"
102
+ WIIM_EQ_LOAD = "EQLoad:{}"
100
103
 
101
104
 
102
105
  class LinkPlayTcpUartCommand(StrEnum):
@@ -219,11 +222,33 @@ class LoopMode(StrEnum):
219
222
  class EqualizerMode(StrEnum):
220
223
  """Defines the equalizer mode."""
221
224
 
222
- NONE = "0"
223
- CLASSIC = "1"
224
- POP = "2"
225
- JAZZ = "3"
226
- VOCAL = "4"
225
+ NONE = "None"
226
+ CLASSIC = "Classic"
227
+ POP = "Pop"
228
+ JAZZ = "Jazz"
229
+ VOCAL = "Vocal"
230
+
231
+ # Custom equalizer modes specificly for WiiM devices
232
+ FLAT = "Flat"
233
+ ACOUSTIC = "Acoustic"
234
+ BASS_BOOSTER = "Bass Booster"
235
+ BASS_REDUCER = "Bass Reducer"
236
+ CLASSICAL = "Classical"
237
+ DANCE = "Dance"
238
+ DEEP = "Deep"
239
+ ELECTRONIC = "Electronic"
240
+ HIP_HOP = "Hip-Hop"
241
+ LATIN = "Latin"
242
+ LOUDNESS = "Loudness"
243
+ LOUNGE = "Lounge"
244
+ PIANO = "Piano"
245
+ R_B = "R&B"
246
+ ROCK = "Rock"
247
+ SMALL_SPEAKERS = "Small Speakers"
248
+ SPOKEN_WORD = "Spoken Word"
249
+ TREBLE_BOOSTER = "Treble Booster"
250
+ TREBLE_REDUCER = "Treble Reducer"
251
+ VOCAL_BOOSTER = "Vocal Booster"
227
252
 
228
253
 
229
254
  class PlayingStatus(StrEnum):
@@ -0,0 +1,74 @@
1
+ """Manufacturers and their devices for the LinkPlay component."""
2
+
3
+ from typing import Final
4
+
5
+ MANUFACTURER_ARTSOUND: Final[str] = "ArtSound"
6
+ MANUFACTURER_ARYLIC: Final[str] = "Arylic"
7
+ MANUFACTURER_IEAST: Final[str] = "iEAST"
8
+ MANUFACTURER_WIIM: Final[str] = "WiiM"
9
+ MANUFACTURER_GGMM: Final[str] = "GGMM"
10
+ MANUFACTURER_MEDION: Final[str] = "Medion"
11
+ MANUFACTURER_GENERIC: Final[str] = "Generic"
12
+ MODELS_ARTSOUND_SMART_ZONE4: Final[str] = "Smart Zone 4 AMP"
13
+ MODELS_ARTSOUND_SMART_HYDE: Final[str] = "Smart Hyde"
14
+ MODELS_ARYLIC_S50: Final[str] = "S50+"
15
+ MODELS_ARYLIC_S50_PRO: Final[str] = "S50 Pro"
16
+ MODELS_ARYLIC_A30: Final[str] = "A30"
17
+ MODELS_ARYLIC_A50: Final[str] = "A50"
18
+ MODELS_ARYLIC_A50S: Final[str] = "A50+"
19
+ MODELS_ARYLIC_UP2STREAM_AMP: Final[str] = "Up2Stream Amp 2.0"
20
+ MODELS_ARYLIC_UP2STREAM_AMP_2P1: Final[str] = "Up2Stream Amp 2.1"
21
+ MODELS_ARYLIC_UP2STREAM_AMP_V3: Final[str] = "Up2Stream Amp v3"
22
+ MODELS_ARYLIC_UP2STREAM_AMP_V4: Final[str] = "Up2Stream Amp v4"
23
+ MODELS_ARYLIC_UP2STREAM_PRO: Final[str] = "Up2Stream Pro v1"
24
+ MODELS_ARYLIC_UP2STREAM_PRO_V3: Final[str] = "Up2Stream Pro v3"
25
+ MODELS_ARYLIC_S10P: Final[str] = "Arylic S10+"
26
+ MODELS_ARYLIC_UP2STREAM_PLATE_AMP: Final[str] = "Up2Stream Plate Amp"
27
+ MODELS_IEAST_AUDIOCAST_M5: Final[str] = "AudioCast M5"
28
+ MODELS_WIIM_AMP: Final[str] = "WiiM Amp"
29
+ MODELS_WIIM_MINI: Final[str] = "WiiM Mini"
30
+ MODELS_WIIM_PRO: Final[str] = "WiiM Pro"
31
+ MODELS_GGMM_GGMM_E2: Final[str] = "GGMM E2"
32
+ MODELS_MEDION_MD_43970: Final[str] = "Life P66970 (MD 43970)"
33
+ MODELS_GENERIC: Final[str] = "Generic"
34
+
35
+ PROJECTID_LOOKUP: Final[dict[str, tuple[str, str]]] = {
36
+ "SMART_ZONE4_AMP": (MANUFACTURER_ARTSOUND, MODELS_ARTSOUND_SMART_ZONE4),
37
+ "SMART_HYDE": (MANUFACTURER_ARTSOUND, MODELS_ARTSOUND_SMART_HYDE),
38
+ "ARYLIC_S50": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_S50),
39
+ "RP0016_S50PRO_S": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_S50_PRO),
40
+ "RP0011_WB60_S": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_A30),
41
+ "X-50": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_A50),
42
+ "ARYLIC_A50S": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_A50S),
43
+ "RP0011_WB60": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP),
44
+ "UP2STREAM_AMP_V3": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP_V3),
45
+ "UP2STREAM_AMP_V4": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP_V4),
46
+ "UP2STREAM_PRO_V3": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_PRO_V3),
47
+ "S10P_WIFI": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_S10P),
48
+ "ARYLIC_V20": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_PLATE_AMP),
49
+ "UP2STREAM_MINI_V3": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
50
+ "UP2STREAM_AMP_2P1": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP_2P1),
51
+ "RP0014_A50C_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
52
+ "ARYLIC_A30": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
53
+ "ARYLIC_SUBWOOFER": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
54
+ "ARYLIC_S50A": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
55
+ "RP0010_D5_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
56
+ "RP0001": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
57
+ "RP0013_WA31S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
58
+ "RP0010_D5": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
59
+ "RP0013_WA31S_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
60
+ "RP0014_A50D_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
61
+ "ARYLIC_A50TE": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
62
+ "ARYLIC_A50N": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
63
+ "iEAST-02": (MANUFACTURER_IEAST, MODELS_IEAST_AUDIOCAST_M5),
64
+ "WiiM_Amp_4layer": (MANUFACTURER_WIIM, MODELS_WIIM_AMP),
65
+ "WiiM_Pro_with_gc4a": (MANUFACTURER_WIIM, MODELS_WIIM_PRO),
66
+ "Muzo_Mini": (MANUFACTURER_WIIM, MODELS_WIIM_MINI),
67
+ "GGMM_E2A": (MANUFACTURER_GGMM, MODELS_GGMM_GGMM_E2),
68
+ "A16": (MANUFACTURER_MEDION, MODELS_MEDION_MD_43970),
69
+ }
70
+
71
+
72
+ def get_info_from_project(project: str) -> tuple[str, str]:
73
+ """Get manufacturer and model info based on given project."""
74
+ return PROJECTID_LOOKUP.get(project, (MANUFACTURER_GENERIC, MODELS_GENERIC))
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python_linkplay
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: A Python Library for Seamless LinkPlay Device Control
5
5
  Author: Velleman Group nv
6
6
  License: MIT
@@ -24,12 +24,13 @@ Requires-Dist: ruff>=0.5.4; extra == "testing"
24
24
  Requires-Dist: tox>=4.6.0; extra == "testing"
25
25
  Requires-Dist: typing-extensions>=4.6.3; extra == "testing"
26
26
  Requires-Dist: pre-commit>=3.8.0; extra == "testing"
27
-
27
+ Dynamic: license-file
28
28
 
29
29
  [![PyPI package](https://badge.fury.io/py/python-linkplay.svg)](https://pypi.org/project/python-linkplay/)
30
30
  [![Release](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml/badge.svg)](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml)
31
31
 
32
32
  # python-linkplay
33
+
33
34
  A Python Library for Seamless LinkPlay Device Control
34
35
 
35
36
  ## Intro
@@ -54,6 +55,9 @@ Welcome to python-linkplay, a powerful and user-friendly Python library designed
54
55
  - https://developer.arylic.com/httpapi/
55
56
  - http://airscope-audio.net/core2/pdf/airscope-module-http.pdf
56
57
  - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Mini.pdf
58
+ - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Products.pdf
59
+ - https://github.com/DanBrezeanu/wiim-extended-http-api
60
+ - https://github.com/cvdlinden/wiim-httpapi
57
61
 
58
62
  ## Multiroom
59
63
 
@@ -0,0 +1,16 @@
1
+ linkplay/__init__.py,sha256=y9ZehEq-KhS3cwn-PUpwVSJGfDUx7e5wf_G6guODcTk,56
2
+ linkplay/__main__.py,sha256=Wcza80QaWfOaHjyJEfQYhB9kiPLE0NOqIj4zVWv2Nqs,577
3
+ linkplay/__version__.py,sha256=FVHPBGkfhbQDi_z3v0PiKJrXXqXOx0vGW_1VaqNJi7U,22
4
+ linkplay/bridge.py,sha256=0Y1AwSUxeaUb9gFcjzZWMi5JOtRkMDyMLnEQ5sK_B4U,19207
5
+ linkplay/consts.py,sha256=3yUX_a_wOjpVLUPK5ogSCEOwgVJs-TIPb8LPo4Ds1Ic,14242
6
+ linkplay/controller.py,sha256=ZW3WbOhm1fzFwkkNTEAfZjBhJkhkd61LtfiFgWXqjE0,3903
7
+ linkplay/discovery.py,sha256=NnkO9gknp3Cyff7830zBu1LwyhkkWCdhDbEDbAwF8D8,4610
8
+ linkplay/endpoint.py,sha256=_gelW0cDWt0SC8EApwaKIVh_YJIMNiOkfrLR_RYW7aM,2677
9
+ linkplay/exceptions.py,sha256=Kow13uJPSL4y6rXMnkcl_Yp9wH1weOyKw_knd0p-Exc,173
10
+ linkplay/manufacturers.py,sha256=oddlZTbtrCBVCcy5adbXk0bhBOXEcmC6U5-a7QShLHY,3906
11
+ linkplay/utils.py,sha256=Kmbzw8zC9mV89ZOC5-GNtbiLkgUkuvAEUcsJdRkSY-w,8485
12
+ python_linkplay-0.2.0.dist-info/licenses/LICENSE,sha256=bgEtxMyjEHX_4uwaAY3GCFTm234D4AOZ5dM15sk26ms,1073
13
+ python_linkplay-0.2.0.dist-info/METADATA,sha256=_W168qwtmKLvvHzEYfXfj1GCLQDbIUMoW2NOB1aYZfc,3179
14
+ python_linkplay-0.2.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
15
+ python_linkplay-0.2.0.dist-info/top_level.txt,sha256=CpSaOVPTzJf5TVIL7MrotSCR34gcIOQy-11l4zGmxxM,9
16
+ python_linkplay-0.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,15 +0,0 @@
1
- linkplay/__init__.py,sha256=y9ZehEq-KhS3cwn-PUpwVSJGfDUx7e5wf_G6guODcTk,56
2
- linkplay/__main__.py,sha256=Wcza80QaWfOaHjyJEfQYhB9kiPLE0NOqIj4zVWv2Nqs,577
3
- linkplay/__version__.py,sha256=mdp2CftfqYbdKtP-eWv1z7rAUycYv6X1ntXSMUf8Kss,22
4
- linkplay/bridge.py,sha256=YyqucmFzqAtmc3ttrQEM4nvMhs6ijxHrAhcDIzH6EE0,14970
5
- linkplay/consts.py,sha256=98VtgV4xOfXWr00yl1DwfmNxDI6Ul5fNPCyLp8ixggE,13535
6
- linkplay/controller.py,sha256=ZW3WbOhm1fzFwkkNTEAfZjBhJkhkd61LtfiFgWXqjE0,3903
7
- linkplay/discovery.py,sha256=NnkO9gknp3Cyff7830zBu1LwyhkkWCdhDbEDbAwF8D8,4610
8
- linkplay/endpoint.py,sha256=_gelW0cDWt0SC8EApwaKIVh_YJIMNiOkfrLR_RYW7aM,2677
9
- linkplay/exceptions.py,sha256=Kow13uJPSL4y6rXMnkcl_Yp9wH1weOyKw_knd0p-Exc,173
10
- linkplay/utils.py,sha256=Kmbzw8zC9mV89ZOC5-GNtbiLkgUkuvAEUcsJdRkSY-w,8485
11
- python_linkplay-0.1.2.dist-info/LICENSE,sha256=bgEtxMyjEHX_4uwaAY3GCFTm234D4AOZ5dM15sk26ms,1073
12
- python_linkplay-0.1.2.dist-info/METADATA,sha256=V13eNH0yWqs2wGC32EhDbKt6X4K68p6oPFHhG7knv-4,2987
13
- python_linkplay-0.1.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
14
- python_linkplay-0.1.2.dist-info/top_level.txt,sha256=CpSaOVPTzJf5TVIL7MrotSCR34gcIOQy-11l4zGmxxM,9
15
- python_linkplay-0.1.2.dist-info/RECORD,,