pyrekordbox 0.3.2__py3-none-any.whl → 0.4.1__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 +9 -4
- pyrekordbox/anlz/__init__.py +3 -2
- pyrekordbox/anlz/file.py +4 -2
- pyrekordbox/anlz/tags.py +8 -16
- pyrekordbox/config.py +116 -47
- pyrekordbox/db6/__init__.py +2 -2
- pyrekordbox/db6/aux_files.py +3 -2
- pyrekordbox/db6/database.py +130 -177
- pyrekordbox/db6/registry.py +1 -0
- pyrekordbox/db6/smartlist.py +9 -11
- pyrekordbox/db6/tables.py +112 -132
- pyrekordbox/logger.py +0 -1
- pyrekordbox/mysettings/__init__.py +5 -4
- pyrekordbox/mysettings/file.py +3 -1
- pyrekordbox/rbxml.py +8 -12
- pyrekordbox/utils.py +4 -3
- {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.dist-info}/METADATA +35 -48
- pyrekordbox-0.4.1.dist-info/RECORD +25 -0
- {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.dist-info}/WHEEL +1 -1
- {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.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 -189
- 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/xml.py +0 -8
- pyrekordbox-0.3.2.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 -1193
- tests/test_mysetting.py +0 -203
- tests/test_xml.py +0 -629
- {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.dist-info/licenses}/LICENSE +0 -0
pyrekordbox/db6/tables.py
CHANGED
@@ -5,15 +5,28 @@
|
|
5
5
|
"""Rekordbox 6 `master.db` SQLAlchemy table declarations."""
|
6
6
|
|
7
7
|
import math
|
8
|
+
import re
|
8
9
|
import struct
|
9
|
-
import numpy as np
|
10
|
-
from enum import IntEnum
|
11
10
|
from datetime import datetime
|
12
|
-
from
|
13
|
-
from
|
14
|
-
|
11
|
+
from enum import IntEnum
|
12
|
+
from typing import List
|
13
|
+
|
14
|
+
import numpy as np
|
15
|
+
from sqlalchemy import (
|
16
|
+
VARCHAR,
|
17
|
+
BigInteger,
|
18
|
+
Column,
|
19
|
+
Float,
|
20
|
+
ForeignKey,
|
21
|
+
Integer,
|
22
|
+
SmallInteger,
|
23
|
+
Text,
|
24
|
+
TypeDecorator,
|
25
|
+
)
|
15
26
|
from sqlalchemy.ext.associationproxy import association_proxy
|
16
27
|
from sqlalchemy.inspection import inspect
|
28
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, backref, mapped_column, relationship
|
29
|
+
|
17
30
|
from .registry import RekordboxAgentRegistry
|
18
31
|
|
19
32
|
__all__ = [
|
@@ -102,34 +115,53 @@ TABLES = [
|
|
102
115
|
]
|
103
116
|
|
104
117
|
|
118
|
+
def datetime_to_str(value: datetime) -> str:
|
119
|
+
s = value.isoformat().replace("T", " ")
|
120
|
+
if value.tzinfo is not None:
|
121
|
+
# Get the timezone info (last 6 characters of the string)
|
122
|
+
tzinfo = s[-6:]
|
123
|
+
s = s[:-9] + " " + tzinfo
|
124
|
+
else:
|
125
|
+
s = s[:-3] + " +00:00"
|
126
|
+
return s
|
127
|
+
|
128
|
+
|
129
|
+
def string_to_datetime(value: str) -> datetime:
|
130
|
+
try:
|
131
|
+
dt = datetime.fromisoformat(value)
|
132
|
+
except ValueError:
|
133
|
+
if len(value.strip()) > 23:
|
134
|
+
# Assume the format
|
135
|
+
# "2025-04-12 19:11:29.274 -05:00" or
|
136
|
+
# "2025-04-12 19:11:29.274 -05:00 (Central Daylight Time)"
|
137
|
+
datestr, tzinfo = value[:23], value[23:30]
|
138
|
+
datestr = datestr.strip()
|
139
|
+
tzinfo = tzinfo.strip()
|
140
|
+
assert re.match(r"^[+-]?\d{1,2}:\d{2}", tzinfo)
|
141
|
+
datestr = datestr.strip() + tzinfo
|
142
|
+
else:
|
143
|
+
datestr, tzinfo = value, ""
|
144
|
+
dt = datetime.fromisoformat(datestr)
|
145
|
+
# Convert to local timezone and return without timezone
|
146
|
+
return dt.astimezone().replace(tzinfo=None)
|
147
|
+
|
148
|
+
|
105
149
|
class DateTime(TypeDecorator):
|
106
150
|
"""Custom datetime column with timezone support.
|
107
151
|
|
108
152
|
The datetime format in the database is `YYYY-MM-DD HH:MM:SS.SSS +00:00`.
|
109
|
-
The timezone seems to always be `+00:00` (UTC).
|
110
153
|
This format is not supported by the `DateTime` column of SQLAlchemy 2.
|
111
154
|
"""
|
112
155
|
|
113
156
|
impl = Text
|
114
157
|
cache_ok = True
|
115
158
|
|
116
|
-
def process_bind_param(self, value, dialect):
|
117
|
-
return value
|
159
|
+
def process_bind_param(self, value: datetime, dialect) -> str:
|
160
|
+
return datetime_to_str(value)
|
118
161
|
|
119
|
-
def process_result_value(self, value, dialect):
|
162
|
+
def process_result_value(self, value: str, dialect):
|
120
163
|
if value:
|
121
|
-
|
122
|
-
dt = datetime.fromisoformat(value)
|
123
|
-
except ValueError:
|
124
|
-
if len(value.strip()) > 23:
|
125
|
-
datestr, tzinfo = value[:23], value[23:]
|
126
|
-
datestr = datestr.strip()
|
127
|
-
tzinfo = tzinfo.strip()
|
128
|
-
assert tzinfo == "+00:00", tzinfo
|
129
|
-
else:
|
130
|
-
datestr, tzinfo = value, ""
|
131
|
-
dt = datetime.fromisoformat(datestr)
|
132
|
-
return dt
|
164
|
+
return string_to_datetime(value)
|
133
165
|
return None
|
134
166
|
|
135
167
|
|
@@ -139,6 +171,15 @@ class PlaylistType(IntEnum):
|
|
139
171
|
SMART_PLAYLIST = 4
|
140
172
|
|
141
173
|
|
174
|
+
class FileType(IntEnum):
|
175
|
+
MP3 = 1
|
176
|
+
M4A = 4
|
177
|
+
FLAC = 5
|
178
|
+
WAV = 11
|
179
|
+
AIFF = 12
|
180
|
+
AIF = 12
|
181
|
+
|
182
|
+
|
142
183
|
# -- Base- and Mixin classes -----------------------------------------------------------
|
143
184
|
|
144
185
|
|
@@ -146,6 +187,7 @@ class Base(DeclarativeBase):
|
|
146
187
|
"""Base class used to initialize the declarative base for all tables."""
|
147
188
|
|
148
189
|
__tablename__: str
|
190
|
+
__keys__: List[str] = []
|
149
191
|
|
150
192
|
@classmethod
|
151
193
|
def create(cls, **kwargs):
|
@@ -164,13 +206,23 @@ class Base(DeclarativeBase):
|
|
164
206
|
"""Returns a list of all relationship names."""
|
165
207
|
return [column.key for column in inspect(cls).relationships] # noqa
|
166
208
|
|
209
|
+
@classmethod
|
210
|
+
def __get_keys__(cls):
|
211
|
+
"""Get all attributes of the table."""
|
212
|
+
items = cls.__dict__.items()
|
213
|
+
keys = [k for k, v in items if not callable(v) and not k.startswith("_")]
|
214
|
+
return keys
|
215
|
+
|
216
|
+
@classmethod
|
217
|
+
def keys(cls):
|
218
|
+
"""Returns a list of all column names including the relationships."""
|
219
|
+
if not cls.__keys__: # Cache the keys
|
220
|
+
cls.__keys__ = cls.__get_keys__()
|
221
|
+
return cls.__keys__
|
222
|
+
|
167
223
|
def __iter__(self):
|
168
224
|
"""Iterates over all columns and relationship names."""
|
169
|
-
|
170
|
-
for column in insp.c:
|
171
|
-
yield column.name
|
172
|
-
for column in insp.relationships: # noqa
|
173
|
-
yield column.key
|
225
|
+
return iter(self.keys())
|
174
226
|
|
175
227
|
def __len__(self):
|
176
228
|
return sum(1 for _ in self.__iter__())
|
@@ -184,10 +236,6 @@ class Base(DeclarativeBase):
|
|
184
236
|
RekordboxAgentRegistry.on_update(self, key, value)
|
185
237
|
super().__setattr__(key, value)
|
186
238
|
|
187
|
-
def keys(self):
|
188
|
-
"""Returns a list of all column names including the relationships."""
|
189
|
-
return list(self.__iter__())
|
190
|
-
|
191
239
|
def values(self):
|
192
240
|
"""Returns a list of all column values including the relationships."""
|
193
241
|
return [self.__getitem__(key) for key in self.keys()]
|
@@ -212,9 +260,7 @@ class Base(DeclarativeBase):
|
|
212
260
|
class StatsTime:
|
213
261
|
"""Mixin class for tables that only use time statistics columns."""
|
214
262
|
|
215
|
-
created_at: Mapped[datetime] = mapped_column(
|
216
|
-
DateTime, nullable=False, default=datetime.now
|
217
|
-
)
|
263
|
+
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.now)
|
218
264
|
"""The creation date of the table entry (from :class:`StatsTime`)."""
|
219
265
|
updated_at: Mapped[datetime] = mapped_column(
|
220
266
|
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now
|
@@ -243,9 +289,7 @@ class StatsFull:
|
|
243
289
|
rb_local_usn: Mapped[int] = mapped_column(BigInteger, default=None)
|
244
290
|
"""The local USN (unique sequence number) of the table entry
|
245
291
|
(from :class:`StatsFull`)."""
|
246
|
-
created_at: Mapped[datetime] = mapped_column(
|
247
|
-
DateTime, nullable=False, default=datetime.now
|
248
|
-
)
|
292
|
+
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.now)
|
249
293
|
"""The creation date of the table entry (from :class:`StatsFull`)."""
|
250
294
|
updated_at: Mapped[datetime] = mapped_column(
|
251
295
|
DateTime, nullable=False, default=datetime.now, onupdate=datetime.now
|
@@ -343,9 +387,7 @@ class ContentActiveCensor(Base, StatsFull):
|
|
343
387
|
|
344
388
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
345
389
|
"""The ID (primary key) of the table entry."""
|
346
|
-
ContentID: Mapped[str] = mapped_column(
|
347
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
348
|
-
)
|
390
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
349
391
|
"""The ID of the :class:`DjmdContent` entry this censor belongs to."""
|
350
392
|
ActiveCensors: Mapped[str] = mapped_column(Text, default=None)
|
351
393
|
"""The active censors of the table entry."""
|
@@ -368,9 +410,7 @@ class ContentCue(Base, StatsFull):
|
|
368
410
|
|
369
411
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
370
412
|
"""The ID (primary key) of the table entry."""
|
371
|
-
ContentID: Mapped[str] = mapped_column(
|
372
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
373
|
-
)
|
413
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
374
414
|
"""The ID of the :class:`DjmdContent` entry this cue belongs to."""
|
375
415
|
Cues: Mapped[str] = mapped_column(Text, default=None)
|
376
416
|
"""The cues of the table entry."""
|
@@ -393,9 +433,7 @@ class ContentFile(Base, StatsFull):
|
|
393
433
|
|
394
434
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
395
435
|
"""The ID (primary key) of the table entry."""
|
396
|
-
ContentID: Mapped[str] = mapped_column(
|
397
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
398
|
-
)
|
436
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
399
437
|
"""The ID of the :class:`DjmdContent` entry this file belongs to."""
|
400
438
|
Path: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
401
439
|
"""The path of the file."""
|
@@ -440,9 +478,7 @@ class DjmdActiveCensor(Base, StatsFull):
|
|
440
478
|
|
441
479
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
442
480
|
"""The ID (primary key) of the table entry."""
|
443
|
-
ContentID: Mapped[str] = mapped_column(
|
444
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
445
|
-
)
|
481
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
446
482
|
"""The ID of the :class:`DjmdContent` entry this censor belongs to."""
|
447
483
|
InMsec: Mapped[int] = mapped_column(Integer, default=None)
|
448
484
|
"""The in time of the censor (in milliseconds)."""
|
@@ -455,9 +491,7 @@ class DjmdActiveCensor(Base, StatsFull):
|
|
455
491
|
ContentUUID: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
456
492
|
"""The UUID of the :class:`DjmdContent` entry this censor belongs to."""
|
457
493
|
|
458
|
-
Content = relationship(
|
459
|
-
"DjmdContent", foreign_keys=ContentID, back_populates="ActiveCensors"
|
460
|
-
)
|
494
|
+
Content = relationship("DjmdContent", foreign_keys=ContentID, back_populates="ActiveCensors")
|
461
495
|
"""The content entry this censor belongs to (links to :class:`DjmdContent`)."""
|
462
496
|
|
463
497
|
|
@@ -588,17 +622,11 @@ class DjmdContent(Base, StatsFull):
|
|
588
622
|
"""The short file name of the file corresponding to the content entry."""
|
589
623
|
Title: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
590
624
|
"""The title of the track."""
|
591
|
-
ArtistID: Mapped[str] = mapped_column(
|
592
|
-
VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None
|
593
|
-
)
|
625
|
+
ArtistID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None)
|
594
626
|
"""The ID of the :class:`DjmdArtist` entry of the artist of this track."""
|
595
|
-
AlbumID: Mapped[str] = mapped_column(
|
596
|
-
VARCHAR(255), ForeignKey("djmdAlbum.ID"), default=None
|
597
|
-
)
|
627
|
+
AlbumID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdAlbum.ID"), default=None)
|
598
628
|
"""The ID of the :class:`DjmdAlbum` entry of the album of this track."""
|
599
|
-
GenreID: Mapped[str] = mapped_column(
|
600
|
-
VARCHAR(255), ForeignKey("djmdGenre.ID"), default=None
|
601
|
-
)
|
629
|
+
GenreID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdGenre.ID"), default=None)
|
602
630
|
"""The ID of the :class:`DjmdGenre` entry of the genre of this track."""
|
603
631
|
BPM: Mapped[int] = mapped_column(Integer, default=None)
|
604
632
|
"""The BPM (beats per minute) of the track."""
|
@@ -618,27 +646,19 @@ class DjmdContent(Base, StatsFull):
|
|
618
646
|
"""The rating of the track."""
|
619
647
|
ReleaseYear: Mapped[int] = mapped_column(Integer, default=None)
|
620
648
|
"""The release year of the track."""
|
621
|
-
RemixerID: Mapped[str] = mapped_column(
|
622
|
-
VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None
|
623
|
-
)
|
649
|
+
RemixerID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None)
|
624
650
|
"""The ID of the :class:`DjmdArtist` entry of the remixer of this track."""
|
625
|
-
LabelID: Mapped[str] = mapped_column(
|
626
|
-
VARCHAR(255), ForeignKey("djmdLabel.ID"), default=None
|
627
|
-
)
|
651
|
+
LabelID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdLabel.ID"), default=None)
|
628
652
|
"""The ID of the :class:`DjmdLabel` entry of the label of this track."""
|
629
653
|
OrgArtistID: Mapped[str] = mapped_column(
|
630
654
|
VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None
|
631
655
|
)
|
632
656
|
"""The ID of the :class:`DjmdArtist` entry of the original artist of this track."""
|
633
|
-
KeyID: Mapped[str] = mapped_column(
|
634
|
-
VARCHAR(255), ForeignKey("djmdKey.ID"), default=None
|
635
|
-
)
|
657
|
+
KeyID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdKey.ID"), default=None)
|
636
658
|
"""The ID of the :class:`DjmdKey` entry of the key of this track."""
|
637
659
|
StockDate: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
638
660
|
"""The stock date of the track."""
|
639
|
-
ColorID: Mapped[str] = mapped_column(
|
640
|
-
VARCHAR(255), ForeignKey("djmdColor.ID"), default=None
|
641
|
-
)
|
661
|
+
ColorID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdColor.ID"), default=None)
|
642
662
|
"""The ID of the :class:`DjmdColor` entry of the color of this track."""
|
643
663
|
DJPlayCount: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
644
664
|
"""The play count of the track."""
|
@@ -656,9 +676,7 @@ class DjmdContent(Base, StatsFull):
|
|
656
676
|
"""The file size of the track."""
|
657
677
|
DiscNo: Mapped[int] = mapped_column(Integer, default=None)
|
658
678
|
"""The number of the disc of the album of the track."""
|
659
|
-
ComposerID: Mapped[str] = mapped_column(
|
660
|
-
VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None
|
661
|
-
)
|
679
|
+
ComposerID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None)
|
662
680
|
"""The ID of the :class:`DjmdArtist` entry of the composer of this track."""
|
663
681
|
Subtitle: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
664
682
|
"""The subtitle of the track."""
|
@@ -690,9 +708,7 @@ class DjmdContent(Base, StatsFull):
|
|
690
708
|
"""The analysis updated status of the track."""
|
691
709
|
TrackInfoUpdated: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
692
710
|
"""The track info updated status of the track."""
|
693
|
-
Lyricist: Mapped[str] = mapped_column(
|
694
|
-
VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None
|
695
|
-
)
|
711
|
+
Lyricist: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdArtist.ID"), default=None)
|
696
712
|
"""The ID of the :class:`DjmdArtist` entry of the lyricist of this track."""
|
697
713
|
ISRC: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
698
714
|
"""The ISRC of the track."""
|
@@ -759,9 +775,7 @@ class DjmdContent(Base, StatsFull):
|
|
759
775
|
"""The album artist entry of the track (links to :class:`DjmdArtist`)."""
|
760
776
|
MyTags = relationship("DjmdSongMyTag", back_populates="Content")
|
761
777
|
"""The my tags of the track (links to :class:`DjmdSongMyTag`)."""
|
762
|
-
Cues = relationship(
|
763
|
-
"DjmdCue", foreign_keys="DjmdCue.ContentID", back_populates="Content"
|
764
|
-
)
|
778
|
+
Cues = relationship("DjmdCue", foreign_keys="DjmdCue.ContentID", back_populates="Content")
|
765
779
|
"""The cues of the track (links to :class:`DjmdCue`)."""
|
766
780
|
ActiveCensors = relationship("DjmdActiveCensor", back_populates="Content")
|
767
781
|
"""The active censors of the track (links to :class:`DjmdActiveCensor`)."""
|
@@ -810,9 +824,7 @@ class DjmdCue(Base, StatsFull):
|
|
810
824
|
|
811
825
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
812
826
|
"""The ID (primary key) of the table entry."""
|
813
|
-
ContentID: Mapped[str] = mapped_column(
|
814
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
815
|
-
)
|
827
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
816
828
|
"""The ID of the content (:class:`DjmdContent`) containing the cue point."""
|
817
829
|
InMsec: Mapped[int] = mapped_column(Integer, default=None)
|
818
830
|
"""The in point of the cue point in milliseconds."""
|
@@ -915,9 +927,7 @@ class DjmdHistory(Base, StatsFull):
|
|
915
927
|
"""The name of the history playlist."""
|
916
928
|
Attribute: Mapped[int] = mapped_column(Integer, default=None)
|
917
929
|
"""The attributes of the history playlist"""
|
918
|
-
ParentID: Mapped[str] = mapped_column(
|
919
|
-
VARCHAR(255), ForeignKey("djmdHistory.ID"), default=None
|
920
|
-
)
|
930
|
+
ParentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdHistory.ID"), default=None)
|
921
931
|
"""The ID of the parent history playlist (:class:`DjmdHistory`)."""
|
922
932
|
DateCreated: Mapped[str] = mapped_column(VARCHAR(255), default=None)
|
923
933
|
"""The date the history playlist was created."""
|
@@ -950,13 +960,9 @@ class DjmdSongHistory(Base, StatsFull):
|
|
950
960
|
|
951
961
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
952
962
|
|
953
|
-
HistoryID: Mapped[str] = mapped_column(
|
954
|
-
VARCHAR(255), ForeignKey("djmdHistory.ID"), default=None
|
955
|
-
)
|
963
|
+
HistoryID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdHistory.ID"), default=None)
|
956
964
|
"""The ID of the history playlist (:class:`DjmdHistory`)."""
|
957
|
-
ContentID: Mapped[str] = mapped_column(
|
958
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
959
|
-
)
|
965
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
960
966
|
"""The ID of the content (:class:`DjmdContent`)."""
|
961
967
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
962
968
|
"""The track number of the song in the history playlist."""
|
@@ -1022,9 +1028,7 @@ class DjmdSongHotCueBanklist(Base, StatsFull):
|
|
1022
1028
|
VARCHAR(255), ForeignKey("djmdHotCueBanklist.ID"), default=None
|
1023
1029
|
)
|
1024
1030
|
"""The ID of the hot cue banklist (:class:`DjmdHotCueBanklist`)."""
|
1025
|
-
ContentID: Mapped[str] = mapped_column(
|
1026
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1027
|
-
)
|
1031
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1028
1032
|
"""The ID of the content (:class:`DjmdContent`)."""
|
1029
1033
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
1030
1034
|
"""The track number of the hot-cue in the hot cue banklist."""
|
@@ -1132,9 +1136,7 @@ class DjmdMixerParam(Base, StatsFull):
|
|
1132
1136
|
|
1133
1137
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
1134
1138
|
"""The ID (primary key) of the table entry."""
|
1135
|
-
ContentID: Mapped[str] = mapped_column(
|
1136
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1137
|
-
)
|
1139
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1138
1140
|
"""The ID of the content (:class:`DjmdContent`)."""
|
1139
1141
|
GainHigh: Mapped[int] = mapped_column(Integer, default=None)
|
1140
1142
|
"""The high gain of the mixer parameter."""
|
@@ -1215,9 +1217,7 @@ class DjmdMyTag(Base, StatsFull):
|
|
1215
1217
|
"""The name of the My-Tag list."""
|
1216
1218
|
Attribute: Mapped[int] = mapped_column(Integer, default=None)
|
1217
1219
|
"""The attribute of the My-Tag list."""
|
1218
|
-
ParentID: Mapped[str] = mapped_column(
|
1219
|
-
VARCHAR(255), ForeignKey("djmdMyTag.ID"), default=None
|
1220
|
-
)
|
1220
|
+
ParentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdMyTag.ID"), default=None)
|
1221
1221
|
"""The ID of the parent My-Tag list (:class:`DjmdMyTag`)."""
|
1222
1222
|
|
1223
1223
|
MyTags = relationship("DjmdSongMyTag", back_populates="MyTag")
|
@@ -1246,13 +1246,9 @@ class DjmdSongMyTag(Base, StatsFull):
|
|
1246
1246
|
|
1247
1247
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
1248
1248
|
"""The ID (primary key) of the table entry."""
|
1249
|
-
MyTagID: Mapped[str] = mapped_column(
|
1250
|
-
VARCHAR(255), ForeignKey("djmdMyTag.ID"), default=None
|
1251
|
-
)
|
1249
|
+
MyTagID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdMyTag.ID"), default=None)
|
1252
1250
|
"""The ID of the My-Tag list (links to :class:`DjmdMyTag`)."""
|
1253
|
-
ContentID: Mapped[str] = mapped_column(
|
1254
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1255
|
-
)
|
1251
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1256
1252
|
"""The ID of the content this item belongs to (:class:`DjmdContent`)."""
|
1257
1253
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
1258
1254
|
"""The track number of the My-Tag item (for ordering)."""
|
@@ -1286,16 +1282,12 @@ class DjmdPlaylist(Base, StatsFull):
|
|
1286
1282
|
"""The path to the image of the playlist."""
|
1287
1283
|
Attribute: Mapped[int] = mapped_column(Integer, default=None)
|
1288
1284
|
"""The attribute of the playlist."""
|
1289
|
-
ParentID: Mapped[str] = mapped_column(
|
1290
|
-
VARCHAR(255), ForeignKey("djmdPlaylist.ID"), default=None
|
1291
|
-
)
|
1285
|
+
ParentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdPlaylist.ID"), default=None)
|
1292
1286
|
"""The ID of the parent playlist (:class:`DjmdPlaylist`)."""
|
1293
1287
|
SmartList: Mapped[str] = mapped_column(Text, default=None)
|
1294
1288
|
"""The smart list settings of the playlist."""
|
1295
1289
|
|
1296
|
-
Songs = relationship(
|
1297
|
-
"DjmdSongPlaylist", back_populates="Playlist", cascade="all, delete"
|
1298
|
-
)
|
1290
|
+
Songs = relationship("DjmdSongPlaylist", back_populates="Playlist", cascade="all, delete")
|
1299
1291
|
"""The contents of the playlist (links to :class:`DjmdSongPlaylist`)."""
|
1300
1292
|
Children = relationship(
|
1301
1293
|
"DjmdPlaylist",
|
@@ -1340,9 +1332,7 @@ class DjmdSongPlaylist(Base, StatsFull):
|
|
1340
1332
|
VARCHAR(255), ForeignKey("djmdPlaylist.ID"), default=None
|
1341
1333
|
)
|
1342
1334
|
"""The ID of the playlist this item is in (:class:`DjmdPlaylist`)."""
|
1343
|
-
ContentID: Mapped[str] = mapped_column(
|
1344
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1345
|
-
)
|
1335
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1346
1336
|
"""The ID of the content this item belongs to (:class:`DjmdContent`)."""
|
1347
1337
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
1348
1338
|
"""The track number of the playlist item (for ordering)."""
|
@@ -1413,9 +1403,7 @@ class DjmdSongRelatedTracks(Base, StatsFull):
|
|
1413
1403
|
)
|
1414
1404
|
"""The ID of the related tracks list this item is in
|
1415
1405
|
(:class:`DjmdRelatedTracks`)."""
|
1416
|
-
ContentID: Mapped[str] = mapped_column(
|
1417
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1418
|
-
)
|
1406
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1419
1407
|
"""The ID of the content this item belongs to (:class:`DjmdContent`)."""
|
1420
1408
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
1421
1409
|
"""The track number of the related tracks list item (for ordering)."""
|
@@ -1444,9 +1432,7 @@ class DjmdSampler(Base, StatsFull):
|
|
1444
1432
|
"""The name of the sampler list."""
|
1445
1433
|
Attribute: Mapped[int] = mapped_column(Integer, default=None)
|
1446
1434
|
"""The attribute of the sampler list."""
|
1447
|
-
ParentID: Mapped[str] = mapped_column(
|
1448
|
-
VARCHAR(255), ForeignKey("djmdSampler.ID"), default=None
|
1449
|
-
)
|
1435
|
+
ParentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdSampler.ID"), default=None)
|
1450
1436
|
"""The ID of the parent sampler list (:class:`DjmdSampler`)."""
|
1451
1437
|
|
1452
1438
|
Songs = relationship("DjmdSongSampler", back_populates="Sampler")
|
@@ -1477,13 +1463,9 @@ class DjmdSongSampler(Base, StatsFull):
|
|
1477
1463
|
|
1478
1464
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
1479
1465
|
"""The ID (primary key) of the table entry."""
|
1480
|
-
SamplerID: Mapped[str] = mapped_column(
|
1481
|
-
VARCHAR(255), ForeignKey("djmdSampler.ID"), default=None
|
1482
|
-
)
|
1466
|
+
SamplerID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdSampler.ID"), default=None)
|
1483
1467
|
"""The ID of the sampler list this item is in (:class:`DjmdSampler`)."""
|
1484
|
-
ContentID: Mapped[str] = mapped_column(
|
1485
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1486
|
-
)
|
1468
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1487
1469
|
"""The ID of the content this item belongs to (:class:`DjmdContent`)."""
|
1488
1470
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
1489
1471
|
"""The track number of the sampler list item (for ordering)."""
|
@@ -1501,9 +1483,7 @@ class DjmdSongTagList(Base, StatsFull):
|
|
1501
1483
|
|
1502
1484
|
ID: Mapped[str] = mapped_column(VARCHAR(255), primary_key=True)
|
1503
1485
|
"""The ID (primary key) of the table entry."""
|
1504
|
-
ContentID: Mapped[str] = mapped_column(
|
1505
|
-
VARCHAR(255), ForeignKey("djmdContent.ID"), default=None
|
1506
|
-
)
|
1486
|
+
ContentID: Mapped[str] = mapped_column(VARCHAR(255), ForeignKey("djmdContent.ID"), default=None)
|
1507
1487
|
"""The ID of the content this item belongs to (:class:`DjmdContent`)."""
|
1508
1488
|
TrackNo: Mapped[int] = mapped_column(Integer, default=None)
|
1509
1489
|
"""The track number of the tag list item (for ordering)."""
|
pyrekordbox/logger.py
CHANGED
@@ -4,14 +4,15 @@
|
|
4
4
|
|
5
5
|
import re
|
6
6
|
from pathlib import Path
|
7
|
+
|
7
8
|
from . import structs
|
8
9
|
from .file import (
|
9
10
|
FILES,
|
10
|
-
SettingsFile,
|
11
|
-
MySettingFile,
|
12
|
-
MySetting2File,
|
13
|
-
DjmMySettingFile,
|
14
11
|
DevSettingFile,
|
12
|
+
DjmMySettingFile,
|
13
|
+
MySetting2File,
|
14
|
+
MySettingFile,
|
15
|
+
SettingsFile,
|
15
16
|
)
|
16
17
|
|
17
18
|
RE_MYSETTING = re.compile(".*SETTING[0-9]?.DAT$")
|
pyrekordbox/mysettings/file.py
CHANGED
pyrekordbox/rbxml.py
CHANGED
@@ -7,10 +7,12 @@ r"""Rekordbox XML database file handler."""
|
|
7
7
|
import logging
|
8
8
|
import os.path
|
9
9
|
import urllib.parse
|
10
|
+
import xml.etree.cElementTree as xml
|
10
11
|
from abc import abstractmethod
|
11
12
|
from collections import abc
|
12
|
-
|
13
|
+
|
13
14
|
import bidict
|
15
|
+
|
14
16
|
from .utils import pretty_xml
|
15
17
|
|
16
18
|
logger = logging.getLogger(__name__)
|
@@ -25,9 +27,7 @@ POSMARK_TYPE_MAPPING = bidict.bidict(
|
|
25
27
|
"4": "loop",
|
26
28
|
}
|
27
29
|
)
|
28
|
-
RATING_MAPPING = bidict.bidict(
|
29
|
-
{"0": 0, "51": 1, "102": 2, "153": 3, "204": 4, "255": 5}
|
30
|
-
)
|
30
|
+
RATING_MAPPING = bidict.bidict({"0": 0, "51": 1, "102": 2, "153": 3, "204": 4, "255": 5})
|
31
31
|
NODE_KEYTYPE_MAPPING = bidict.bidict({"0": "TrackID", "1": "Location"})
|
32
32
|
|
33
33
|
|
@@ -271,9 +271,7 @@ class Tempo(AbstractElement):
|
|
271
271
|
ATTRIBS = ["Inizio", "Bpm", "Metro", "Battito"]
|
272
272
|
GETTERS = {"Inizio": float, "Bpm": float, "Battito": int}
|
273
273
|
|
274
|
-
def __init__(
|
275
|
-
self, parent=None, Inizio=0.0, Bpm=0.0, Metro="4/4", Battito=1, element=None
|
276
|
-
):
|
274
|
+
def __init__(self, parent=None, Inizio=0.0, Bpm=0.0, Metro="4/4", Battito=1, element=None):
|
277
275
|
super().__init__(element, parent, Inizio, Bpm, Metro, Battito)
|
278
276
|
|
279
277
|
def _init(self, parent, inizio, bpm, metro, battito):
|
@@ -658,7 +656,7 @@ class Node:
|
|
658
656
|
|
659
657
|
@property
|
660
658
|
def key_type(self):
|
661
|
-
"""str: The type of key used by the playlist node"""
|
659
|
+
"""str: The type of key used by the playlist node."""
|
662
660
|
return NODE_KEYTYPE_MAPPING.get(self._element.attrib.get("KeyType"))
|
663
661
|
|
664
662
|
@property
|
@@ -1262,9 +1260,7 @@ class RekordboxXml:
|
|
1262
1260
|
num_tracks = len(self._collection.findall(f".//{Track.TAG}"))
|
1263
1261
|
n = int(self._collection.attrib["Entries"])
|
1264
1262
|
if n != num_tracks:
|
1265
|
-
raise ValueError(
|
1266
|
-
f"Track count {num_tracks} does not match number of elements {n}"
|
1267
|
-
)
|
1263
|
+
raise ValueError(f"Track count {num_tracks} does not match number of elements {n}")
|
1268
1264
|
# Generate XML string
|
1269
1265
|
return pretty_xml(self._root, indent, encoding="utf-8")
|
1270
1266
|
|
@@ -1280,7 +1276,7 @@ class RekordboxXml:
|
|
1280
1276
|
The default is 3 spaces.
|
1281
1277
|
"""
|
1282
1278
|
string = self.tostring(indent)
|
1283
|
-
with open(path, "w") as fh:
|
1279
|
+
with open(path, "w", encoding="utf-8") as fh:
|
1284
1280
|
fh.write(string)
|
1285
1281
|
|
1286
1282
|
def __repr__(self):
|
pyrekordbox/utils.py
CHANGED
@@ -6,9 +6,10 @@
|
|
6
6
|
|
7
7
|
import os
|
8
8
|
import warnings
|
9
|
-
import psutil
|
10
|
-
from xml.dom import minidom
|
11
9
|
import xml.etree.cElementTree as xml
|
10
|
+
from xml.dom import minidom
|
11
|
+
|
12
|
+
import psutil
|
12
13
|
|
13
14
|
warnings.simplefilter("always", DeprecationWarning)
|
14
15
|
|
@@ -122,7 +123,7 @@ def get_rekordbox_agent_pid(raise_exec=False):
|
|
122
123
|
|
123
124
|
|
124
125
|
def pretty_xml(element, indent=None, encoding="utf-8"):
|
125
|
-
"""Generates a formatted string of an XML element.
|
126
|
+
r"""Generates a formatted string of an XML element.
|
126
127
|
|
127
128
|
Parameters
|
128
129
|
----------
|