pyrekordbox 0.3.1__py3-none-any.whl → 0.4.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.
- pyrekordbox/__init__.py +8 -8
- pyrekordbox/__main__.py +3 -2
- pyrekordbox/_version.py +2 -2
- pyrekordbox/anlz/__init__.py +3 -2
- pyrekordbox/anlz/file.py +4 -2
- pyrekordbox/anlz/tags.py +3 -1
- pyrekordbox/config.py +79 -23
- pyrekordbox/db6/__init__.py +2 -2
- pyrekordbox/db6/aux_files.py +3 -2
- pyrekordbox/db6/database.py +227 -143
- pyrekordbox/db6/registry.py +1 -0
- pyrekordbox/db6/smartlist.py +375 -0
- pyrekordbox/db6/tables.py +81 -20
- pyrekordbox/logger.py +0 -1
- pyrekordbox/mysettings/__init__.py +5 -4
- pyrekordbox/mysettings/file.py +3 -1
- pyrekordbox/rbxml.py +5 -3
- pyrekordbox/utils.py +4 -3
- {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/LICENSE +1 -1
- {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/METADATA +26 -42
- pyrekordbox-0.4.0.dist-info/RECORD +25 -0
- {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/WHEEL +1 -1
- {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/top_level.txt +0 -2
- docs/Makefile +0 -20
- docs/make.bat +0 -35
- docs/source/_static/images/anlz_beat.svg +0 -53
- docs/source/_static/images/anlz_file.svg +0 -204
- docs/source/_static/images/anlz_pco2.svg +0 -138
- docs/source/_static/images/anlz_pcob.svg +0 -148
- docs/source/_static/images/anlz_pcp2.svg +0 -398
- docs/source/_static/images/anlz_pcpt.svg +0 -263
- docs/source/_static/images/anlz_ppth.svg +0 -123
- docs/source/_static/images/anlz_pqt2.svg +0 -324
- docs/source/_static/images/anlz_pqt2_2.svg +0 -253
- docs/source/_static/images/anlz_pqtz.svg +0 -140
- docs/source/_static/images/anlz_pssi.svg +0 -192
- docs/source/_static/images/anlz_pssi_entry.svg +0 -191
- docs/source/_static/images/anlz_pvbr.svg +0 -125
- docs/source/_static/images/anlz_pwav.svg +0 -130
- docs/source/_static/images/anlz_pwv3.svg +0 -139
- docs/source/_static/images/anlz_pwv4.svg +0 -139
- docs/source/_static/images/anlz_pwv5.svg +0 -139
- docs/source/_static/images/anlz_pwv5_entry.svg +0 -100
- docs/source/_static/images/anlz_pwv6.svg +0 -130
- docs/source/_static/images/anlz_pwv7.svg +0 -139
- docs/source/_static/images/anlz_pwvc.svg +0 -125
- docs/source/_static/images/anlz_tag.svg +0 -110
- docs/source/_static/images/x64dbg_rb_key.png +0 -0
- docs/source/_static/logos/dark/logo_primary.svg +0 -75
- docs/source/_static/logos/light/logo_primary.svg +0 -75
- docs/source/_static/logos/mid/logo_primary.svg +0 -75
- docs/source/_templates/apidoc/module.rst_t +0 -8
- docs/source/_templates/apidoc/package.rst_t +0 -57
- docs/source/_templates/apidoc/toc.rst_t +0 -7
- docs/source/_templates/autosummary/class.rst +0 -32
- docs/source/_templates/autosummary/module.rst +0 -55
- docs/source/api.md +0 -18
- docs/source/conf.py +0 -178
- docs/source/development/changes.md +0 -3
- docs/source/development/contributing.md +0 -3
- docs/source/formats/anlz.md +0 -634
- docs/source/formats/db6.md +0 -1233
- docs/source/formats/mysetting.md +0 -392
- docs/source/formats/xml.md +0 -376
- docs/source/index.md +0 -103
- docs/source/installation.md +0 -271
- docs/source/key.md +0 -103
- docs/source/quickstart.md +0 -185
- docs/source/requirements.txt +0 -7
- docs/source/tutorial/anlz.md +0 -7
- docs/source/tutorial/configuration.md +0 -66
- docs/source/tutorial/db6.md +0 -178
- docs/source/tutorial/index.md +0 -20
- docs/source/tutorial/mysetting.md +0 -124
- docs/source/tutorial/xml.md +0 -140
- pyrekordbox/db6/smart_playlist.py +0 -333
- pyrekordbox/xml.py +0 -8
- pyrekordbox-0.3.1.dist-info/RECORD +0 -84
- tests/__init__.py +0 -3
- tests/test_anlz.py +0 -206
- tests/test_config.py +0 -175
- tests/test_db6.py +0 -1115
- tests/test_mysetting.py +0 -203
- tests/test_xml.py +0 -629
@@ -1,333 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2023-12-13
|
4
|
-
|
5
|
-
import xml.etree.cElementTree as xml
|
6
|
-
from enum import IntEnum
|
7
|
-
from typing import List, Union
|
8
|
-
from datetime import datetime
|
9
|
-
from dataclasses import dataclass
|
10
|
-
|
11
|
-
from sqlalchemy import or_, and_, not_
|
12
|
-
from sqlalchemy.sql.elements import BooleanClauseList
|
13
|
-
from dateutil.relativedelta import relativedelta # noqa
|
14
|
-
|
15
|
-
from .tables import DjmdContent
|
16
|
-
|
17
|
-
__all__ = [
|
18
|
-
"LogicalOperator",
|
19
|
-
"Operator",
|
20
|
-
"Condition",
|
21
|
-
"SmartList",
|
22
|
-
]
|
23
|
-
|
24
|
-
|
25
|
-
class LogicalOperator(IntEnum):
|
26
|
-
ALL = 1
|
27
|
-
ANY = 2
|
28
|
-
|
29
|
-
|
30
|
-
class Operator(IntEnum):
|
31
|
-
EQUAL = 1
|
32
|
-
NOT_EQUAL = 2
|
33
|
-
GREATER = 3
|
34
|
-
LESS = 4
|
35
|
-
IN_RANGE = 5
|
36
|
-
IN_LAST = 6
|
37
|
-
NOT_IN_LAST = 7
|
38
|
-
CONTAINS = 8
|
39
|
-
NOT_CONTAINS = 9
|
40
|
-
STARTS_WITH = 10
|
41
|
-
ENDS_WITH = 11
|
42
|
-
|
43
|
-
|
44
|
-
STR_OPS = [
|
45
|
-
Operator.EQUAL,
|
46
|
-
Operator.NOT_EQUAL,
|
47
|
-
Operator.CONTAINS,
|
48
|
-
Operator.NOT_CONTAINS,
|
49
|
-
Operator.STARTS_WITH,
|
50
|
-
Operator.ENDS_WITH,
|
51
|
-
]
|
52
|
-
|
53
|
-
NUM_OPS = [
|
54
|
-
Operator.EQUAL,
|
55
|
-
Operator.NOT_EQUAL,
|
56
|
-
Operator.GREATER,
|
57
|
-
Operator.LESS,
|
58
|
-
Operator.IN_RANGE,
|
59
|
-
]
|
60
|
-
|
61
|
-
DATE_OPS = [
|
62
|
-
Operator.EQUAL,
|
63
|
-
Operator.NOT_EQUAL,
|
64
|
-
Operator.GREATER,
|
65
|
-
Operator.LESS,
|
66
|
-
Operator.IN_RANGE,
|
67
|
-
Operator.IN_LAST,
|
68
|
-
Operator.NOT_IN_LAST,
|
69
|
-
]
|
70
|
-
|
71
|
-
|
72
|
-
PROPERTY_MAP = {
|
73
|
-
"artist": "ArtistName",
|
74
|
-
"album": "AlbumName",
|
75
|
-
"albumArtist": "AlbumArtist",
|
76
|
-
"originalArtist": "OrgArtist",
|
77
|
-
"bpm": "BPM",
|
78
|
-
"grouping": "ColorID",
|
79
|
-
"comments": "Commnt",
|
80
|
-
"producer": "ComposerName",
|
81
|
-
"stockDate": "StockDate",
|
82
|
-
"dateCreated": "created_at",
|
83
|
-
"counter": "DJPlayCount",
|
84
|
-
"fileName": "FileNameL",
|
85
|
-
"genre": "GenreName",
|
86
|
-
"key": "Key",
|
87
|
-
"label": "LabelName",
|
88
|
-
"mixName": "",
|
89
|
-
"myTag": "myTag",
|
90
|
-
"rating": "Rating",
|
91
|
-
"dateReleased": "ReleaseDate",
|
92
|
-
"remixedBy": "RemixerName",
|
93
|
-
"duration": "Length",
|
94
|
-
"name": "Title",
|
95
|
-
"year": "ReleaseYear",
|
96
|
-
}
|
97
|
-
|
98
|
-
|
99
|
-
VALID_OPS = {
|
100
|
-
"artist": STR_OPS,
|
101
|
-
"album": STR_OPS,
|
102
|
-
"albumArtist": STR_OPS,
|
103
|
-
"originalArtist": STR_OPS,
|
104
|
-
"bpm": NUM_OPS,
|
105
|
-
"grouping": [Operator.EQUAL, Operator.NOT_EQUAL],
|
106
|
-
"comments": STR_OPS,
|
107
|
-
"producer": STR_OPS,
|
108
|
-
"stockDate": DATE_OPS,
|
109
|
-
"dateCreated": DATE_OPS,
|
110
|
-
"counter": NUM_OPS,
|
111
|
-
"fileName": STR_OPS,
|
112
|
-
"genre": STR_OPS,
|
113
|
-
"key": STR_OPS,
|
114
|
-
"label": STR_OPS,
|
115
|
-
"mixName": STR_OPS,
|
116
|
-
"myTag": [Operator.CONTAINS, Operator.NOT_CONTAINS],
|
117
|
-
"rating": NUM_OPS,
|
118
|
-
"dateReleased": DATE_OPS,
|
119
|
-
"remixedBy": STR_OPS,
|
120
|
-
"duration": NUM_OPS,
|
121
|
-
"name": STR_OPS,
|
122
|
-
"year": NUM_OPS,
|
123
|
-
}
|
124
|
-
|
125
|
-
TYPE_CONVERSION = {
|
126
|
-
"bpm": int,
|
127
|
-
"stockDate": lambda x: datetime.strptime(x, "%Y-%m-%d"),
|
128
|
-
"dateCreated": lambda x: datetime.strptime(x, "%Y-%m-%d"),
|
129
|
-
"counter": int,
|
130
|
-
"rating": int,
|
131
|
-
"dateReleased": lambda x: datetime.strptime(x, "%Y-%m-%d"),
|
132
|
-
"duration": int,
|
133
|
-
"year": int,
|
134
|
-
}
|
135
|
-
|
136
|
-
|
137
|
-
@dataclass
|
138
|
-
class Condition:
|
139
|
-
"""Dataclass for a smart playlist condition."""
|
140
|
-
|
141
|
-
property: str
|
142
|
-
operator: int
|
143
|
-
unit: str
|
144
|
-
value_left: Union[str, int]
|
145
|
-
value_right: Union[str, int]
|
146
|
-
|
147
|
-
def __post_init__(self):
|
148
|
-
if self.property not in VALID_OPS:
|
149
|
-
raise ValueError(f"Invalid property: '{self.property}'")
|
150
|
-
|
151
|
-
valid_ops = VALID_OPS[self.property]
|
152
|
-
if self.operator not in valid_ops:
|
153
|
-
raise ValueError(
|
154
|
-
f"Invalid operator '{self.operator}' for '{self.property}', "
|
155
|
-
f"must be one of {valid_ops}"
|
156
|
-
)
|
157
|
-
|
158
|
-
if self.operator == Operator.IN_RANGE:
|
159
|
-
if not self.value_right:
|
160
|
-
raise ValueError(f"Operator '{self.operator}' requires `value_right`")
|
161
|
-
|
162
|
-
|
163
|
-
def _get_condition_values(cond):
|
164
|
-
val_left = cond.value_left
|
165
|
-
val_right = cond.value_right
|
166
|
-
func = None
|
167
|
-
if cond.operator in (Operator.IN_LAST, Operator.NOT_IN_LAST):
|
168
|
-
func = int
|
169
|
-
elif cond.property in TYPE_CONVERSION:
|
170
|
-
func = TYPE_CONVERSION[cond.property]
|
171
|
-
|
172
|
-
if func is not None:
|
173
|
-
if val_left != "":
|
174
|
-
val_left = func(val_left)
|
175
|
-
if val_right != "":
|
176
|
-
try:
|
177
|
-
val_right = func(val_right)
|
178
|
-
except ValueError:
|
179
|
-
pass
|
180
|
-
return val_left, val_right
|
181
|
-
|
182
|
-
|
183
|
-
class SmartList:
|
184
|
-
"""Rekordbox smart playlist XML handler."""
|
185
|
-
|
186
|
-
def __init__(
|
187
|
-
self,
|
188
|
-
playlist_id: Union[int, str] = None,
|
189
|
-
logical_operator: int = None,
|
190
|
-
auto_update: int = 1,
|
191
|
-
):
|
192
|
-
self.playlist_id: Union[int, str] = playlist_id
|
193
|
-
self.logical_operator: int = logical_operator
|
194
|
-
self.auto_update: int = auto_update
|
195
|
-
self.conditions: List[Condition] = list()
|
196
|
-
|
197
|
-
def parse(self, source: str) -> None:
|
198
|
-
"""Parse the XML source of a smart playlist."""
|
199
|
-
tree = xml.ElementTree(xml.fromstring(source))
|
200
|
-
root = tree.getroot()
|
201
|
-
conditions = list()
|
202
|
-
for child in root.findall("CONDITION"):
|
203
|
-
condition = Condition(
|
204
|
-
property=child.attrib["PropertyName"],
|
205
|
-
operator=int(child.attrib["Operator"]),
|
206
|
-
unit=child.attrib["ValueUnit"],
|
207
|
-
value_left=child.attrib["ValueLeft"],
|
208
|
-
value_right=child.attrib["ValueRight"],
|
209
|
-
)
|
210
|
-
conditions.append(condition)
|
211
|
-
|
212
|
-
self.playlist_id = int(root.attrib["Id"])
|
213
|
-
self.logical_operator = int(root.attrib["LogicalOperator"])
|
214
|
-
self.auto_update = int(root.attrib["AutomaticUpdate"])
|
215
|
-
self.conditions = conditions
|
216
|
-
|
217
|
-
def to_xml(self) -> str:
|
218
|
-
"""Convert the smart playlist conditions to XML."""
|
219
|
-
attrib = {
|
220
|
-
"Id": str(self.playlist_id),
|
221
|
-
"LogicalOperator": str(self.logical_operator),
|
222
|
-
"AutomaticUpdate": str(self.auto_update),
|
223
|
-
}
|
224
|
-
root = xml.Element("NODE", attrib=attrib)
|
225
|
-
for cond in self.conditions:
|
226
|
-
attrib = {
|
227
|
-
"PropertyName": str(cond.property),
|
228
|
-
"Operator": str(cond.operator),
|
229
|
-
"ValueUnit": str(cond.unit),
|
230
|
-
"ValueLeft": str(cond.value_left),
|
231
|
-
"ValueRight": str(cond.value_right),
|
232
|
-
}
|
233
|
-
xml.SubElement(root, "CONDITION", attrib=attrib)
|
234
|
-
return xml.tostring(root).decode("utf-8").replace(" /", "/")
|
235
|
-
|
236
|
-
def add_condition(
|
237
|
-
self,
|
238
|
-
prop: str,
|
239
|
-
operator: int,
|
240
|
-
value_left: str,
|
241
|
-
value_right: str = "",
|
242
|
-
unit: str = "",
|
243
|
-
) -> None:
|
244
|
-
"""Add a condition to the smart playlist.
|
245
|
-
|
246
|
-
Parameters
|
247
|
-
----------
|
248
|
-
prop : str
|
249
|
-
The property to filter on.
|
250
|
-
operator : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} int
|
251
|
-
The operator to use. Must be in the range of 1-11
|
252
|
-
value_left : str
|
253
|
-
The left value to use.
|
254
|
-
value_right : str, optional
|
255
|
-
The right value to use, by default "".
|
256
|
-
unit : str, optional
|
257
|
-
The unit to use, by default "".
|
258
|
-
"""
|
259
|
-
cond = Condition(prop, operator, unit, value_left, value_right)
|
260
|
-
self.conditions.append(cond)
|
261
|
-
|
262
|
-
def filter_clause(self) -> BooleanClauseList:
|
263
|
-
"""Return a SQLAlchemy filter clause matching the content of the smart playlist.
|
264
|
-
|
265
|
-
Returns
|
266
|
-
-------
|
267
|
-
BooleanClauseList
|
268
|
-
A filter list macthing the contents of the smart playlist.
|
269
|
-
"""
|
270
|
-
conditions = self.conditions
|
271
|
-
logical_op = and_ if self.logical_operator == LogicalOperator.ALL else or_
|
272
|
-
cond = conditions[0]
|
273
|
-
|
274
|
-
colum_name = PROPERTY_MAP[cond.property]
|
275
|
-
|
276
|
-
comps = list()
|
277
|
-
for cond in conditions:
|
278
|
-
val_left, val_right = _get_condition_values(cond)
|
279
|
-
|
280
|
-
if cond.operator == Operator.EQUAL:
|
281
|
-
comp = getattr(DjmdContent, colum_name) == val_left
|
282
|
-
|
283
|
-
elif cond.operator == Operator.NOT_EQUAL:
|
284
|
-
comp = getattr(DjmdContent, colum_name) != val_left
|
285
|
-
|
286
|
-
elif cond.operator == Operator.GREATER:
|
287
|
-
comp = getattr(DjmdContent, colum_name) > val_left
|
288
|
-
|
289
|
-
elif cond.operator == Operator.LESS:
|
290
|
-
comp = getattr(DjmdContent, colum_name) < val_left
|
291
|
-
|
292
|
-
elif cond.operator == Operator.IN_RANGE:
|
293
|
-
comp = getattr(DjmdContent, colum_name).between(val_left, val_right)
|
294
|
-
|
295
|
-
elif cond.operator == Operator.CONTAINS:
|
296
|
-
comp = getattr(DjmdContent, colum_name).contains(val_left)
|
297
|
-
|
298
|
-
elif cond.operator == Operator.NOT_CONTAINS:
|
299
|
-
comp = not_(getattr(DjmdContent, colum_name).contains(val_left))
|
300
|
-
|
301
|
-
elif cond.operator == Operator.STARTS_WITH:
|
302
|
-
comp = getattr(DjmdContent, colum_name).startswith(val_left)
|
303
|
-
|
304
|
-
elif cond.operator == Operator.ENDS_WITH:
|
305
|
-
comp = getattr(DjmdContent, colum_name).endswith(val_left)
|
306
|
-
|
307
|
-
elif cond.operator == Operator.IN_LAST:
|
308
|
-
now = datetime.now()
|
309
|
-
if cond.unit == "day":
|
310
|
-
t0 = now - relativedelta(days=val_left)
|
311
|
-
comp = getattr(DjmdContent, colum_name) > t0
|
312
|
-
elif cond.unit == "month":
|
313
|
-
t0 = now - relativedelta(months=val_left)
|
314
|
-
comp = getattr(DjmdContent, colum_name).month > t0
|
315
|
-
else:
|
316
|
-
raise ValueError(f"Unknown unit '{cond.unit}'")
|
317
|
-
|
318
|
-
elif cond.operator == Operator.NOT_IN_LAST:
|
319
|
-
now = datetime.now()
|
320
|
-
if cond.unit == "day":
|
321
|
-
t0 = now - relativedelta(days=val_left)
|
322
|
-
comp = getattr(DjmdContent, colum_name) < t0
|
323
|
-
elif cond.unit == "month":
|
324
|
-
t0 = now - relativedelta(months=val_left)
|
325
|
-
comp = getattr(DjmdContent, colum_name).month < t0
|
326
|
-
else:
|
327
|
-
raise ValueError(f"Unknown unit '{cond.unit}'")
|
328
|
-
|
329
|
-
else:
|
330
|
-
raise ValueError(f"Unknown operator '{cond.operator}'")
|
331
|
-
|
332
|
-
comps.append(comp)
|
333
|
-
return logical_op(*comps)
|
pyrekordbox/xml.py
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
docs/Makefile,sha256=4zv3TVkTACm6JBaKgTES3ZI9cETXgM6ULbZkXZP1as8,638
|
2
|
-
docs/make.bat,sha256=s8EuuVXNRnn4xmWLWTpk3Z01aqJSJT8ymrmK6ux0zbc,769
|
3
|
-
docs/source/api.md,sha256=IKKMeQ2wFSS_ippOWJ63ELtMjfkNe27KjWcFFEdUXnE,276
|
4
|
-
docs/source/conf.py,sha256=lPfAJtd0VY7uEDmHrEfHvQzNJg6WN_1seM9pCFc2e6E,6244
|
5
|
-
docs/source/index.md,sha256=c67TMpDCaiqFe44n0pXWL__utlLAz7xqfDVjyWbxx3M,2660
|
6
|
-
docs/source/installation.md,sha256=WmX6gjuPimA6uXhS7wZzKKG1AQCHpfB0nPm3g8_ur6A,9234
|
7
|
-
docs/source/key.md,sha256=J9kx2Cgwm-ddLjbFkjBmAargPSg5WfVguJ1ffrDuCDs,3976
|
8
|
-
docs/source/quickstart.md,sha256=kKau0nQHc0RByrVq0pgaDKWTUX_l4290ruqY08mXTCg,6301
|
9
|
-
docs/source/requirements.txt,sha256=7YYc-tO0BQcBVUa4GvM1r1G5Ma9gkwGZpNbAKlJL97s,117
|
10
|
-
docs/source/_static/images/anlz_beat.svg,sha256=cVooi17HFw3mOT39cFNB05tr-cZwbK_Hjmho6eBJNX8,2924
|
11
|
-
docs/source/_static/images/anlz_file.svg,sha256=E74hjyTFJRy6tCxyTPfsYoM0GmOKoY1To_eHatpPEjs,12320
|
12
|
-
docs/source/_static/images/anlz_pco2.svg,sha256=LAv_EZf72FbslKdHmuBy5ROWy8xKKkkXqp7mSk3WG20,8155
|
13
|
-
docs/source/_static/images/anlz_pcob.svg,sha256=w4NRLCj0EMMnQ2i6tnlE-nFxETSiTs8PQF5nxMI3bgo,8682
|
14
|
-
docs/source/_static/images/anlz_pcp2.svg,sha256=JDncv7fo6ylorWyaH7kZ6roYB7zryFbJE_HY4IfWG8E,24904
|
15
|
-
docs/source/_static/images/anlz_pcpt.svg,sha256=8PcHBFAHXrDnTyC_7rO0TM-5cGy8BfjIXWxDi0zEK8Y,16473
|
16
|
-
docs/source/_static/images/anlz_ppth.svg,sha256=-rPlwvBrrsYaL87GNI-WJJBFCYkcZwkWoGwH5NT8bLk,7124
|
17
|
-
docs/source/_static/images/anlz_pqt2.svg,sha256=sRvbV-DjoTIWtxwChaw6kBqKFXNqBCF4V9hru9ZtdiE,18386
|
18
|
-
docs/source/_static/images/anlz_pqt2_2.svg,sha256=8g3XCJDz8xbH_KK7CvMpmIZjaGrWBKQVlA4k4M3PF50,13774
|
19
|
-
docs/source/_static/images/anlz_pqtz.svg,sha256=OPlW76hPkbhOByYgmVQfucTpb5irZXYcASZokfpK3A0,8141
|
20
|
-
docs/source/_static/images/anlz_pssi.svg,sha256=zM8fsNshSMd0wDvO_qM7RzsAKVIEN8Y41jY76hBJjyQ,11684
|
21
|
-
docs/source/_static/images/anlz_pssi_entry.svg,sha256=YkUg7fXtUst0OBI6vIlhHCK-nzhmR-22UGKFQ0pHxxA,12095
|
22
|
-
docs/source/_static/images/anlz_pvbr.svg,sha256=7mA6S__F4AoX-GamftlevellDiTlcIWRWi77A7HZEDg,7284
|
23
|
-
docs/source/_static/images/anlz_pwav.svg,sha256=psm3g7ILLVNgPNmPG2Hy7k9-q7qmnMnyQ3_-XYVAW2g,7566
|
24
|
-
docs/source/_static/images/anlz_pwv3.svg,sha256=FwZJIiHj5hj-FB-cioNlxrGBeIEuZy2BOLEzSWC57IE,8389
|
25
|
-
docs/source/_static/images/anlz_pwv4.svg,sha256=k5GQu2N965zPSG0FQWuml96UMfqBvs8Vy-Emklvn2dI,8173
|
26
|
-
docs/source/_static/images/anlz_pwv5.svg,sha256=Ph1TSlji2BgWK3rVI7MvnpGM68wef1s-Hs6OlmC0oQQ,8173
|
27
|
-
docs/source/_static/images/anlz_pwv5_entry.svg,sha256=TyNh_6gIsQKuDvS0RO5tmsptzmxiQ-LL1L3DQdIFi4I,5648
|
28
|
-
docs/source/_static/images/anlz_pwv6.svg,sha256=J2KvE_bI-NTFSpFp7QsKSsgbWGCyuUAuW_dOgdnv2DY,7577
|
29
|
-
docs/source/_static/images/anlz_pwv7.svg,sha256=n5NsTErvfsCw-otWdVXn12lM0tHyh-zQDZsZtQOJf0U,8173
|
30
|
-
docs/source/_static/images/anlz_pwvc.svg,sha256=wO_tsxRDVnAbptbSZYK1z7YOH-0vNUqXDB9Udj4ungA,7128
|
31
|
-
docs/source/_static/images/anlz_tag.svg,sha256=owPTtrTRDYCkaSg32_UJ-MTZp3K6WlkRQdJHTzdtxqs,6188
|
32
|
-
docs/source/_static/images/x64dbg_rb_key.png,sha256=gtwUA6lbuFRbLQQACnBQ-aO8p5Ex-7E41dS0cW6XyAU,280011
|
33
|
-
docs/source/_static/logos/dark/logo_primary.svg,sha256=pH4Q28u9JaCA4dVrKB-NDy00PNw2EhDwz7fIYUzezvk,3079
|
34
|
-
docs/source/_static/logos/light/logo_primary.svg,sha256=MUbYjowOcWdtoHVnK7pyV9xcTRdm7gS4whw94pVMGSI,3080
|
35
|
-
docs/source/_static/logos/mid/logo_primary.svg,sha256=utCWWbXXW5auRRposMMaMO9t_AflsN13rZGo3_Pg2WE,3078
|
36
|
-
docs/source/_templates/apidoc/module.rst_t,sha256=keQqb9G8LA-JS5tuytgzfsxRXsADEvuOz3mJjY9qYHU,185
|
37
|
-
docs/source/_templates/apidoc/package.rst_t,sha256=Ra-lunZcMyDtrb6vgrfAnBcBW4eFMTEIyg12fSe9h4o,1173
|
38
|
-
docs/source/_templates/apidoc/toc.rst_t,sha256=6EfpNwcRXLa8Ae3NcovuSmV5PSBy5gP0t6XfphUYb7M,127
|
39
|
-
docs/source/_templates/autosummary/class.rst,sha256=5yRMWkUNDXGvQE1gGm6yisL1bX4BDOsaBPciNUs_6vA,579
|
40
|
-
docs/source/_templates/autosummary/module.rst,sha256=xSCcWeBqQdMgpese2OwFlvzSOMsPZKTBCLCwgD3OG4k,789
|
41
|
-
docs/source/development/changes.md,sha256=ys1_zvYKQWEak_mBXBUMLpO3CWriHpjSAg1-17mTk04,40
|
42
|
-
docs/source/development/contributing.md,sha256=7bERN03kHo2Wf0ZrgHYP4MfBSdFGmE_Kk0CxK0uay2I,43
|
43
|
-
docs/source/formats/anlz.md,sha256=P7FrPqbu3i8SlRKVV9IU9cOQajKJHOjLlAWHheDGN7c,21455
|
44
|
-
docs/source/formats/db6.md,sha256=fuB1_PaalHV4zRkhXh5_mn1ZgHfPnSu_OzUOYbVls6w,30121
|
45
|
-
docs/source/formats/mysetting.md,sha256=OIYIn_B2HJcJtJM0wNjjzPy3xPkyz4raxg_-Y6E3D1k,9746
|
46
|
-
docs/source/formats/xml.md,sha256=uL7eWWQfQXsyf55Vydt1iLq7Jzio3kIGbSfjpg5-CRY,8491
|
47
|
-
docs/source/tutorial/anlz.md,sha256=JtcQIy44qaSP_GL0aL9VYQYOX8aVju7uFxgN3FPNy9o,145
|
48
|
-
docs/source/tutorial/configuration.md,sha256=mqmeFM3I7pDA_ajPQWqVyvc9Y0PWGYXLr3yyojTGsL8,2127
|
49
|
-
docs/source/tutorial/db6.md,sha256=nmaC6sF1noBxp0_LvihnzD9eMYeL-fCkAet4qiShOcc,6611
|
50
|
-
docs/source/tutorial/index.md,sha256=zjx5uXESYzHIyeqtL4XjhZUDPOpNiutZ3gkUOeG8R9w,318
|
51
|
-
docs/source/tutorial/mysetting.md,sha256=su-WMvEd3DrFF32RDfQBm3TlAUgK5PRPT1n_77BYnvY,3659
|
52
|
-
docs/source/tutorial/xml.md,sha256=x_rFvsVzZtUV0upEkPWkgrmr3B-FYjZKWvBuJBLFAu0,6414
|
53
|
-
pyrekordbox/__init__.py,sha256=ODYrBuSF14PNKNAxsbi5_VR-ja2P3ySK0WwM8l7MFCk,647
|
54
|
-
pyrekordbox/__main__.py,sha256=R1lus_-hBG4Uf6SRuzXXFwaE-dT9Uma_lzMOGgsdDLs,5975
|
55
|
-
pyrekordbox/_version.py,sha256=HzPz9rq3s1AiZXregKlqKaJJ2wGMtvH_a3V9la9CnpM,411
|
56
|
-
pyrekordbox/config.py,sha256=ttihHzwfg3IGnp2IyCdnOWKhiH72HZ-dy7NQb4-SG-8,25190
|
57
|
-
pyrekordbox/logger.py,sha256=qCY_3L_3WIMAvNmVKvh7oshGlWrd0T5aIW1bNCIu3Lo,521
|
58
|
-
pyrekordbox/rbxml.py,sha256=0qsKc8SQa54URwWGt6oHTOHBiAQU2S8MbSxDbzKUB7A,38369
|
59
|
-
pyrekordbox/utils.py,sha256=sT1xt-rM94Dir-S0OSSqKbquf9kdFbzZvBu3-7q0QDM,4361
|
60
|
-
pyrekordbox/xml.py,sha256=7wFkMyWL4AQLINvxVxkQxlruG9c7R-gkxdP7xXFOkWU,241
|
61
|
-
pyrekordbox/anlz/__init__.py,sha256=OSpyl3pmmnt3mG905J9qvNnYchsgrIfathw0QXoOhjo,3156
|
62
|
-
pyrekordbox/anlz/file.py,sha256=r2FppO6efPzKfCfz6foX9G2aBu8gkKXMrjIm9fua3_M,6949
|
63
|
-
pyrekordbox/anlz/structs.py,sha256=Lt4fkb3SAE8w146eWeWGnpgRoP6jhLMWrSMoMwPjG04,7925
|
64
|
-
pyrekordbox/anlz/tags.py,sha256=uqneBP9CstYy7a4IpeEC9A5tfI59DQm0ik7yFXk_dNo,14094
|
65
|
-
pyrekordbox/db6/__init__.py,sha256=d2ITtFzw41QfaI_v6QPYpr2KxgziGdsVyxOdXTvlPH0,886
|
66
|
-
pyrekordbox/db6/aux_files.py,sha256=t0SfMCXBHjlCSfgRfpw1RTj0cmV0QY07xSLepjQqMnY,7591
|
67
|
-
pyrekordbox/db6/database.py,sha256=dO2v7JNBMLDt7g22vLDmegzP3TYeeYiBnQqE5P5q8AE,78252
|
68
|
-
pyrekordbox/db6/registry.py,sha256=L4X49HqKtvyD_c4Ad3HR7aM5bj468MgD3Yj6WUE0ri8,9724
|
69
|
-
pyrekordbox/db6/smart_playlist.py,sha256=BOZrPHUU1r0_dM3fwLn8flBi5tigeScENoB3RFoyHiY,9911
|
70
|
-
pyrekordbox/db6/tables.py,sha256=EnfZscPVVeVKmVTFmDoXT3sHMcfjgtq8j-GIMpElMuU,66326
|
71
|
-
pyrekordbox/mysettings/__init__.py,sha256=rMS6Kknf1-X3PXF_TUxm8xui0H1Ap3R7G_9mqlnQUAM,705
|
72
|
-
pyrekordbox/mysettings/file.py,sha256=dQEsdBivpverXCssyVNUeQyUXIooNrOGIIiPLwi_6T4,12669
|
73
|
-
pyrekordbox/mysettings/structs.py,sha256=5Y1F3qTmsP1fRB39_BEHpQVxKx2DO9BytEuJUG_RNcY,8472
|
74
|
-
tests/__init__.py,sha256=s321RtRiHJsLt0YFq0NRncW7u66uwYHQE-6IjPcg10o,67
|
75
|
-
tests/test_anlz.py,sha256=hZAW5GFEJpU8UmmHYLuU5OZlApGrWZyPAR79KtaR0cw,5782
|
76
|
-
tests/test_config.py,sha256=wYp4wHRhIk2grhGXhk4z9umrhOWhVvTOm2mHydzLEs8,6157
|
77
|
-
tests/test_db6.py,sha256=O19L1nZiQX492ts3uY3yU6dprzcH7WTUobJKb4HErFA,37411
|
78
|
-
tests/test_mysetting.py,sha256=te6B_BErGc6ThgBpAl45n0Nd0hWJcm-X9WTw14bR6MA,5781
|
79
|
-
tests/test_xml.py,sha256=SMRML85D24AMpgqBASK1rd-5UdacTlyUVWZGpOoRp40,16854
|
80
|
-
pyrekordbox-0.3.1.dist-info/LICENSE,sha256=Au8sngdQ79q5JsZXZJVU13j46VDxFbPwC4x6sSz8Jes,1074
|
81
|
-
pyrekordbox-0.3.1.dist-info/METADATA,sha256=5OaFdFRXPPnvMKhbifLndCDzWvi74uwq3offFgT25z0,16720
|
82
|
-
pyrekordbox-0.3.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
83
|
-
pyrekordbox-0.3.1.dist-info/top_level.txt,sha256=Cv8QDfcJ7y8fYUm0Q8D5GoiGxaqb7qt8Z5ntVbj1cLk,23
|
84
|
-
pyrekordbox-0.3.1.dist-info/RECORD,,
|
tests/__init__.py
DELETED
tests/test_anlz.py
DELETED
@@ -1,206 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2023-02-01
|
4
|
-
|
5
|
-
import os
|
6
|
-
import pytest
|
7
|
-
import numpy as np
|
8
|
-
from numpy.testing import assert_equal
|
9
|
-
from pyrekordbox import anlz
|
10
|
-
|
11
|
-
TEST_ROOT = os.path.join(os.path.dirname(os.path.dirname(__file__)), ".testdata")
|
12
|
-
ANLZ_ROOT = os.path.join(TEST_ROOT, "export", "PIONEER", "USBANLZ")
|
13
|
-
ANLZ_DIRS = list(anlz.walk_anlz_paths(ANLZ_ROOT))
|
14
|
-
ANLZ_FILES = [paths for _, paths in ANLZ_DIRS]
|
15
|
-
|
16
|
-
|
17
|
-
def test_parse():
|
18
|
-
for root, files in ANLZ_DIRS:
|
19
|
-
for path in files.values():
|
20
|
-
anlz.AnlzFile.parse_file(path)
|
21
|
-
|
22
|
-
|
23
|
-
def test_rebuild():
|
24
|
-
for root, files in ANLZ_DIRS:
|
25
|
-
for path in files.values():
|
26
|
-
file = anlz.AnlzFile.parse_file(path)
|
27
|
-
data = file.build()
|
28
|
-
assert len(data) == file.file_header.len_file
|
29
|
-
_ = anlz.AnlzFile.parse(data)
|
30
|
-
|
31
|
-
|
32
|
-
def test_read_anlz_files():
|
33
|
-
for root, files in ANLZ_DIRS:
|
34
|
-
anlz_files = anlz.read_anlz_files(root)
|
35
|
-
assert len(files) == len(anlz_files)
|
36
|
-
|
37
|
-
|
38
|
-
# -- Tags ------------------------------------------------------------------------------
|
39
|
-
|
40
|
-
|
41
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
42
|
-
def test_pqtz_tag_getters(paths):
|
43
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
44
|
-
tag = file.get_tag("PQTZ")
|
45
|
-
beats, bpms, times = tag.get()
|
46
|
-
nbeats = len(beats)
|
47
|
-
# Check that shape of arrays are equal
|
48
|
-
assert nbeats == len(bpms)
|
49
|
-
assert nbeats == len(times)
|
50
|
-
# Check that the beats array only contains the values 1-4
|
51
|
-
if nbeats:
|
52
|
-
assert_equal(np.sort(np.unique(beats)), [1, 2, 3, 4])
|
53
|
-
|
54
|
-
# Check other getters
|
55
|
-
assert_equal(beats, tag.get_beats())
|
56
|
-
assert_equal(bpms, tag.get_bpms())
|
57
|
-
assert_equal(times, tag.get_times())
|
58
|
-
|
59
|
-
|
60
|
-
def test_pqtz_tag_set_beats():
|
61
|
-
paths = ANLZ_FILES[0]
|
62
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
63
|
-
tag = file.get_tag("PQTZ")
|
64
|
-
|
65
|
-
beats = np.ones(tag.count)
|
66
|
-
tag.set_beats(beats)
|
67
|
-
assert_equal(tag.get_beats(), beats)
|
68
|
-
|
69
|
-
|
70
|
-
def test_pqtz_tag_set_bpms():
|
71
|
-
paths = ANLZ_FILES[0]
|
72
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
73
|
-
tag = file.get_tag("PQTZ")
|
74
|
-
|
75
|
-
bpms = 100 * np.ones(tag.count, dtype=np.float64)
|
76
|
-
tag.set_bpms(bpms)
|
77
|
-
assert_equal(tag.get_bpms(), bpms)
|
78
|
-
|
79
|
-
|
80
|
-
def test_pqtz_tag_set_times():
|
81
|
-
paths = ANLZ_FILES[0]
|
82
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
83
|
-
tag = file.get_tag("PQTZ")
|
84
|
-
|
85
|
-
times = 0.5 * np.arange(tag.count)
|
86
|
-
tag.set_times(times)
|
87
|
-
assert_equal(tag.get_times(), times)
|
88
|
-
|
89
|
-
|
90
|
-
def test_pqtz_tag_set():
|
91
|
-
paths = ANLZ_FILES[0]
|
92
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
93
|
-
tag = file.get_tag("PQTZ")
|
94
|
-
|
95
|
-
beats = np.ones(tag.count)
|
96
|
-
bpms = 100 * np.ones(tag.count, dtype=np.float64)
|
97
|
-
times = 0.5 * np.arange(tag.count)
|
98
|
-
tag.set(beats, bpms, times)
|
99
|
-
assert_equal(tag.get_bpms(), bpms)
|
100
|
-
assert_equal(tag.get_bpms(), bpms)
|
101
|
-
assert_equal(tag.get_times(), times)
|
102
|
-
|
103
|
-
|
104
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
105
|
-
def test_ppth_tag_getters(paths):
|
106
|
-
tmp = ""
|
107
|
-
for path in paths.values():
|
108
|
-
file = anlz.AnlzFile.parse_file(path)
|
109
|
-
tag = file.get_tag("PPTH")
|
110
|
-
p = tag.get()
|
111
|
-
if not tmp:
|
112
|
-
tmp = p
|
113
|
-
else:
|
114
|
-
assert tmp == p
|
115
|
-
|
116
|
-
|
117
|
-
def test_ppth_tag_setters():
|
118
|
-
paths = ANLZ_FILES[0]
|
119
|
-
extected = r"C:/new/path/to/file.mp3"
|
120
|
-
for path in paths.values():
|
121
|
-
file = anlz.AnlzFile.parse_file(path)
|
122
|
-
tag = file.get_tag("PPTH")
|
123
|
-
tag.set(r"C:\new\path\to\file.mp3")
|
124
|
-
assert tag.get() == extected
|
125
|
-
|
126
|
-
|
127
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
128
|
-
def test_pwav_tag_getters(paths):
|
129
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
130
|
-
tag = file.get_tag("PWAV")
|
131
|
-
|
132
|
-
heights, color = tag.get()
|
133
|
-
assert len(heights) == len(color)
|
134
|
-
assert np.all(np.logical_and(0 <= heights, heights <= 31))
|
135
|
-
assert np.all(np.logical_and(0 <= color, color <= 7))
|
136
|
-
|
137
|
-
|
138
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
139
|
-
def test_pwv2_tag_getters(paths):
|
140
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
141
|
-
tag = file.get_tag("PWV2")
|
142
|
-
|
143
|
-
heights, color = tag.get()
|
144
|
-
assert len(heights) == len(color)
|
145
|
-
assert np.all(np.logical_and(0 <= heights, heights <= 31))
|
146
|
-
assert np.all(np.logical_and(0 <= color, color <= 7))
|
147
|
-
|
148
|
-
|
149
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
150
|
-
def test_pwv3_tag_getters(paths):
|
151
|
-
file = anlz.AnlzFile.parse_file(paths["EXT"])
|
152
|
-
tag = file.get_tag("PWV3")
|
153
|
-
|
154
|
-
heights, color = tag.get()
|
155
|
-
assert len(heights) == len(color)
|
156
|
-
assert np.all(np.logical_and(0 <= heights, heights <= 31))
|
157
|
-
assert np.all(np.logical_and(0 <= color, color <= 7))
|
158
|
-
|
159
|
-
|
160
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
161
|
-
def test_pwv4_tag_getters(paths):
|
162
|
-
file = anlz.AnlzFile.parse_file(paths["EXT"])
|
163
|
-
tag = file.get_tag("PWV4")
|
164
|
-
|
165
|
-
heights, colors, blues = tag.get()
|
166
|
-
assert len(heights) == len(colors)
|
167
|
-
assert len(heights) == len(blues)
|
168
|
-
|
169
|
-
|
170
|
-
@pytest.mark.parametrize("paths", ANLZ_FILES)
|
171
|
-
def test_pwv5_tag_getters(paths):
|
172
|
-
file = anlz.AnlzFile.parse_file(paths["EXT"])
|
173
|
-
tag = file.get_tag("PWV5")
|
174
|
-
|
175
|
-
heights, colors = tag.get()
|
176
|
-
assert len(heights) == colors.shape[0]
|
177
|
-
|
178
|
-
|
179
|
-
# -- File ------------------------------------------------------------------------------
|
180
|
-
|
181
|
-
|
182
|
-
def test_anlzfile_getall_tags():
|
183
|
-
paths = ANLZ_FILES[0]
|
184
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
185
|
-
key = "PPTH"
|
186
|
-
tags = file.getall_tags(key)
|
187
|
-
assert len(tags) == 1
|
188
|
-
assert tags[0].get() == file.get(key)
|
189
|
-
|
190
|
-
|
191
|
-
def test_anlzfile_get():
|
192
|
-
paths = ANLZ_FILES[0]
|
193
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
194
|
-
key = "PPTH"
|
195
|
-
tag = file.get_tag(key)
|
196
|
-
assert file.get(key) == tag.get()
|
197
|
-
|
198
|
-
|
199
|
-
def test_anlzfile_getall():
|
200
|
-
paths = ANLZ_FILES[0]
|
201
|
-
file = anlz.AnlzFile.parse_file(paths["DAT"])
|
202
|
-
key = "PPTH"
|
203
|
-
tag = file.get_tag(key)
|
204
|
-
values = file.getall(key)
|
205
|
-
assert len(values) == 1
|
206
|
-
assert values[0] == tag.get()
|