pyrekordbox 0.2.1__py3-none-any.whl → 0.2.2__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.
- docs/source/formats/anlz.md +178 -7
- docs/source/formats/db6.md +1 -1
- docs/source/index.md +2 -6
- docs/source/quickstart.md +68 -45
- docs/source/tutorial/index.md +1 -1
- pyrekordbox/__init__.py +1 -1
- pyrekordbox/_version.py +2 -2
- pyrekordbox/anlz/file.py +39 -0
- pyrekordbox/anlz/structs.py +3 -5
- pyrekordbox/config.py +71 -27
- pyrekordbox/db6/database.py +260 -33
- pyrekordbox/db6/registry.py +22 -0
- pyrekordbox/db6/tables.py +3 -4
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/METADATA +12 -11
- pyrekordbox-0.2.2.dist-info/RECORD +80 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/top_level.txt +0 -2
- tests/test_config.py +175 -0
- tests/test_db6.py +78 -0
- build/lib/build/lib/docs/source/conf.py +0 -178
- build/lib/build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/build/lib/pyrekordbox/_version.py +0 -16
- build/lib/build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/build/lib/pyrekordbox/config.py +0 -596
- build/lib/build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/build/lib/pyrekordbox/logger.py +0 -23
- build/lib/build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/build/lib/pyrekordbox/utils.py +0 -162
- build/lib/build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/build/lib/tests/__init__.py +0 -3
- build/lib/build/lib/tests/test_anlz.py +0 -206
- build/lib/build/lib/tests/test_db6.py +0 -1039
- build/lib/build/lib/tests/test_mysetting.py +0 -203
- build/lib/build/lib/tests/test_xml.py +0 -629
- build/lib/docs/source/conf.py +0 -178
- build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/pyrekordbox/_version.py +0 -16
- build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/pyrekordbox/config.py +0 -596
- build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/pyrekordbox/logger.py +0 -23
- build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/pyrekordbox/utils.py +0 -162
- build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/tests/__init__.py +0 -3
- build/lib/tests/test_anlz.py +0 -206
- build/lib/tests/test_db6.py +0 -1039
- build/lib/tests/test_mysetting.py +0 -203
- build/lib/tests/test_xml.py +0 -629
- pyrekordbox-0.2.1.dist-info/RECORD +0 -129
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/LICENSE +0 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/WHEEL +0 -0
@@ -1,213 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2023-09-10
|
4
|
-
|
5
|
-
from pathlib import Path
|
6
|
-
from datetime import datetime
|
7
|
-
import xml.etree.cElementTree as xml
|
8
|
-
from ..config import get_config
|
9
|
-
from ..utils import pretty_xml
|
10
|
-
|
11
|
-
|
12
|
-
class MasterPlaylistXml:
|
13
|
-
"""Rekordbox v6 masterPlaylists6.xml file handler.
|
14
|
-
|
15
|
-
Rekordbox stores some playlist information in the masterPlaylists6.xml file.
|
16
|
-
Each playlist is represented by a <PLAYLIST> element, containing the following
|
17
|
-
attributes:
|
18
|
-
- Id: The playlist ID in hexadecimal format.
|
19
|
-
- ParentId: The parent playlist ID in hexadecimal format. The root playlist has
|
20
|
-
- Attributes: The type of playlist. 0 = normal, 1 = folder, 4 = smart playlist.
|
21
|
-
- Timestamp: The last time the playlist was updated.
|
22
|
-
- Lib_Type: ? (0 for palylists/folders)
|
23
|
-
- CheckType: ? (always 0)
|
24
|
-
"""
|
25
|
-
|
26
|
-
KEYS = ["Id", "ParentId", "Attributes", "Timestamp", "Lib_Type", "CheckType"]
|
27
|
-
|
28
|
-
def __init__(self, path=None, db_dir=None):
|
29
|
-
if path is None:
|
30
|
-
if db_dir is None:
|
31
|
-
db_dir = get_config("rekordbox6", "db_dir")
|
32
|
-
path = Path(db_dir) / "masterPlaylists6.xml"
|
33
|
-
|
34
|
-
tree = xml.parse(str(path))
|
35
|
-
self.path = path
|
36
|
-
self.root = tree.getroot()
|
37
|
-
self.product = self.root.find("PRODUCT")
|
38
|
-
self.playlists = self.root.find("PLAYLISTS")
|
39
|
-
self._changed = False
|
40
|
-
|
41
|
-
@property
|
42
|
-
def version(self):
|
43
|
-
return self.root.attrib["Version"]
|
44
|
-
|
45
|
-
@property
|
46
|
-
def automatic_sync(self):
|
47
|
-
return self.root.attrib["AutomaticSync"]
|
48
|
-
|
49
|
-
@property
|
50
|
-
def rekordbox_version(self):
|
51
|
-
return self.product.attrib["Version"]
|
52
|
-
|
53
|
-
@property
|
54
|
-
def modified(self):
|
55
|
-
return self._changed
|
56
|
-
|
57
|
-
def get_playlists(self):
|
58
|
-
"""Returns a list of the attributes of all playlist elements."""
|
59
|
-
items = list()
|
60
|
-
for playlist in self.playlists:
|
61
|
-
items.append(playlist.attrib)
|
62
|
-
return items
|
63
|
-
|
64
|
-
def get(self, playlist_id):
|
65
|
-
"""Returns element attribs with the PlaylistID used in the `master.db` database.
|
66
|
-
|
67
|
-
Parameters
|
68
|
-
----------
|
69
|
-
playlist_id : str or int
|
70
|
-
The playlist ID used in the main `master.db` database. This id is converted
|
71
|
-
to hexadecimal format before searching.
|
72
|
-
|
73
|
-
Returns
|
74
|
-
-------
|
75
|
-
playlist : dict
|
76
|
-
"""
|
77
|
-
hex_id = f"{int(playlist_id):X}"
|
78
|
-
element = self.playlists.find(f'.//NODE[@Id="{hex_id}"]')
|
79
|
-
if element is None:
|
80
|
-
return None
|
81
|
-
attribs = dict(element.attrib)
|
82
|
-
attribs["Attribute"] = int(attribs["Attribute"])
|
83
|
-
attribs["Timestamp"] = datetime.fromtimestamp(int(attribs["Timestamp"]) / 1000)
|
84
|
-
attribs["Lib_Type"] = int(attribs["Lib_Type"])
|
85
|
-
attribs["CheckType"] = int(attribs["CheckType"])
|
86
|
-
return attribs
|
87
|
-
|
88
|
-
def add(
|
89
|
-
self,
|
90
|
-
playlist_id: str,
|
91
|
-
parent_id: str,
|
92
|
-
attribute: int,
|
93
|
-
updated_at: datetime,
|
94
|
-
lib_type: int = 0,
|
95
|
-
check_type: int = 0,
|
96
|
-
):
|
97
|
-
"""Adds a new element with the PlaylistID used in the `master.db` database.
|
98
|
-
|
99
|
-
Parameters
|
100
|
-
----------
|
101
|
-
playlist_id : str or int
|
102
|
-
The playlist ID used in the main `master.db` database. This id is converted
|
103
|
-
to hexadecimal format before searching.
|
104
|
-
parent_id : str or int, optional
|
105
|
-
The parent playlist ID used in the main `master.db` database. This id is
|
106
|
-
converted to hexadecimal format.
|
107
|
-
attribute : int, optional
|
108
|
-
The type of playlist. 0 = normal, 1 = folder, 4 = smart playlist.
|
109
|
-
updated_at : datetime, optional
|
110
|
-
The last time the playlist was updated.
|
111
|
-
lib_type : int, optional
|
112
|
-
The libarray type. It seems to be always 0 for playlists.
|
113
|
-
check_type : int, optional
|
114
|
-
The check type. It seems to be always 0.
|
115
|
-
|
116
|
-
Returns
|
117
|
-
-------
|
118
|
-
element : xml.Element
|
119
|
-
The newly created element.
|
120
|
-
"""
|
121
|
-
hex_id = f"{int(playlist_id):X}"
|
122
|
-
parent_id = f"{int(parent_id):X}" if parent_id != "root" else "0"
|
123
|
-
timestamp = int(updated_at.timestamp() * 1000)
|
124
|
-
attrib = {
|
125
|
-
"Id": hex_id,
|
126
|
-
"ParentId": parent_id,
|
127
|
-
"Attribute": str(attribute),
|
128
|
-
"Timestamp": str(timestamp),
|
129
|
-
"Lib_Type": str(lib_type),
|
130
|
-
"CheckType": str(check_type),
|
131
|
-
}
|
132
|
-
if self.playlists.find(f'.//NODE[@Id="{hex_id}"]') is not None:
|
133
|
-
raise ValueError(f"Playlist with ID {playlist_id} ({hex_id}) exists.")
|
134
|
-
element = xml.SubElement(self.playlists, "NODE", attrib=attrib)
|
135
|
-
self._changed = True
|
136
|
-
return element
|
137
|
-
|
138
|
-
def remove(self, playlist_id):
|
139
|
-
"""Removes the element with the PlaylistID used in the `master.db` database.
|
140
|
-
|
141
|
-
Parameters
|
142
|
-
----------
|
143
|
-
playlist_id : str or int
|
144
|
-
The playlist ID used in the main `master.db` database. This id is converted
|
145
|
-
to hexadecimal format before searching.
|
146
|
-
"""
|
147
|
-
hex_id = f"{int(playlist_id):X}"
|
148
|
-
element = self.playlists.find(f'.//NODE[@Id="{hex_id}"]')
|
149
|
-
if element is None:
|
150
|
-
raise ValueError(f"Playlist with ID {playlist_id} ({hex_id}) not found.")
|
151
|
-
self.playlists.remove(element)
|
152
|
-
self._changed = True
|
153
|
-
|
154
|
-
def update(
|
155
|
-
self,
|
156
|
-
playlist_id: str,
|
157
|
-
parent_id: str = None,
|
158
|
-
attribute: int = None,
|
159
|
-
updated_at: datetime = None,
|
160
|
-
lib_type: int = None,
|
161
|
-
check_type: int = None,
|
162
|
-
):
|
163
|
-
"""Updates the element with the PlaylistID used in the `master.db` database.
|
164
|
-
|
165
|
-
Parameters
|
166
|
-
----------
|
167
|
-
playlist_id : str or int
|
168
|
-
The playlist ID used in the main `master.db` database. This id is converted
|
169
|
-
to hexadecimal format before searching.
|
170
|
-
parent_id : str or int, optional
|
171
|
-
The parent playlist ID used in the main `master.db` database. This id is
|
172
|
-
converted to hexadecimal format.
|
173
|
-
attribute : int, optional
|
174
|
-
The type of playlist. 0 = normal, 1 = folder, 4 = smart playlist.
|
175
|
-
updated_at : datetime, optional
|
176
|
-
The last time the playlist was updated.
|
177
|
-
lib_type : int, optional
|
178
|
-
The libarray type. It seems to be always 0 for playlists.
|
179
|
-
check_type : int, optional
|
180
|
-
The check type. It seems to be always 0.
|
181
|
-
"""
|
182
|
-
hex_id = f"{int(playlist_id):X}"
|
183
|
-
element = self.playlists.find(f'.//NODE[@Id="{hex_id}"]')
|
184
|
-
if element is None:
|
185
|
-
raise ValueError(f"Playlist with ID {playlist_id} ({hex_id}) not found.")
|
186
|
-
|
187
|
-
attribs = dict()
|
188
|
-
if parent_id is not None:
|
189
|
-
attribs["ParentId"] = f"{int(parent_id):X}" if parent_id != "root" else "0"
|
190
|
-
if attribute is not None:
|
191
|
-
attribs["Attribute"] = str(attribute)
|
192
|
-
if updated_at is not None:
|
193
|
-
attribs["Timestamp"] = str(int(updated_at.timestamp() * 1000))
|
194
|
-
if lib_type is not None:
|
195
|
-
attribs["Lib_Type"] = str(lib_type)
|
196
|
-
if check_type is not None:
|
197
|
-
attribs["CheckType"] = str(check_type)
|
198
|
-
|
199
|
-
element.attrib.update(attribs)
|
200
|
-
self._changed = True
|
201
|
-
|
202
|
-
def to_string(self, indent=None):
|
203
|
-
return pretty_xml(self.root, indent, encoding="utf-8")
|
204
|
-
|
205
|
-
def save(self, path=None, indent=None):
|
206
|
-
if path is None:
|
207
|
-
path = self.path
|
208
|
-
path = str(path)
|
209
|
-
|
210
|
-
string = self.to_string(indent)
|
211
|
-
with open(path, "w") as fh:
|
212
|
-
fh.write(string)
|
213
|
-
self._changed = False
|