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.
Files changed (83) hide show
  1. pyrekordbox/__init__.py +8 -8
  2. pyrekordbox/__main__.py +3 -2
  3. pyrekordbox/_version.py +9 -4
  4. pyrekordbox/anlz/__init__.py +3 -2
  5. pyrekordbox/anlz/file.py +4 -2
  6. pyrekordbox/anlz/tags.py +8 -16
  7. pyrekordbox/config.py +116 -47
  8. pyrekordbox/db6/__init__.py +2 -2
  9. pyrekordbox/db6/aux_files.py +3 -2
  10. pyrekordbox/db6/database.py +130 -177
  11. pyrekordbox/db6/registry.py +1 -0
  12. pyrekordbox/db6/smartlist.py +9 -11
  13. pyrekordbox/db6/tables.py +112 -132
  14. pyrekordbox/logger.py +0 -1
  15. pyrekordbox/mysettings/__init__.py +5 -4
  16. pyrekordbox/mysettings/file.py +3 -1
  17. pyrekordbox/rbxml.py +8 -12
  18. pyrekordbox/utils.py +4 -3
  19. {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.dist-info}/METADATA +35 -48
  20. pyrekordbox-0.4.1.dist-info/RECORD +25 -0
  21. {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.dist-info}/WHEEL +1 -1
  22. {pyrekordbox-0.3.2.dist-info → pyrekordbox-0.4.1.dist-info}/top_level.txt +0 -2
  23. docs/Makefile +0 -20
  24. docs/make.bat +0 -35
  25. docs/source/_static/images/anlz_beat.svg +0 -53
  26. docs/source/_static/images/anlz_file.svg +0 -204
  27. docs/source/_static/images/anlz_pco2.svg +0 -138
  28. docs/source/_static/images/anlz_pcob.svg +0 -148
  29. docs/source/_static/images/anlz_pcp2.svg +0 -398
  30. docs/source/_static/images/anlz_pcpt.svg +0 -263
  31. docs/source/_static/images/anlz_ppth.svg +0 -123
  32. docs/source/_static/images/anlz_pqt2.svg +0 -324
  33. docs/source/_static/images/anlz_pqt2_2.svg +0 -253
  34. docs/source/_static/images/anlz_pqtz.svg +0 -140
  35. docs/source/_static/images/anlz_pssi.svg +0 -192
  36. docs/source/_static/images/anlz_pssi_entry.svg +0 -191
  37. docs/source/_static/images/anlz_pvbr.svg +0 -125
  38. docs/source/_static/images/anlz_pwav.svg +0 -130
  39. docs/source/_static/images/anlz_pwv3.svg +0 -139
  40. docs/source/_static/images/anlz_pwv4.svg +0 -139
  41. docs/source/_static/images/anlz_pwv5.svg +0 -139
  42. docs/source/_static/images/anlz_pwv5_entry.svg +0 -100
  43. docs/source/_static/images/anlz_pwv6.svg +0 -130
  44. docs/source/_static/images/anlz_pwv7.svg +0 -139
  45. docs/source/_static/images/anlz_pwvc.svg +0 -125
  46. docs/source/_static/images/anlz_tag.svg +0 -110
  47. docs/source/_static/images/x64dbg_rb_key.png +0 -0
  48. docs/source/_static/logos/dark/logo_primary.svg +0 -75
  49. docs/source/_static/logos/light/logo_primary.svg +0 -75
  50. docs/source/_static/logos/mid/logo_primary.svg +0 -75
  51. docs/source/_templates/apidoc/module.rst_t +0 -8
  52. docs/source/_templates/apidoc/package.rst_t +0 -57
  53. docs/source/_templates/apidoc/toc.rst_t +0 -7
  54. docs/source/_templates/autosummary/class.rst +0 -32
  55. docs/source/_templates/autosummary/module.rst +0 -55
  56. docs/source/api.md +0 -18
  57. docs/source/conf.py +0 -178
  58. docs/source/development/changes.md +0 -3
  59. docs/source/development/contributing.md +0 -3
  60. docs/source/formats/anlz.md +0 -634
  61. docs/source/formats/db6.md +0 -1233
  62. docs/source/formats/mysetting.md +0 -392
  63. docs/source/formats/xml.md +0 -376
  64. docs/source/index.md +0 -103
  65. docs/source/installation.md +0 -271
  66. docs/source/key.md +0 -103
  67. docs/source/quickstart.md +0 -189
  68. docs/source/requirements.txt +0 -7
  69. docs/source/tutorial/anlz.md +0 -7
  70. docs/source/tutorial/configuration.md +0 -66
  71. docs/source/tutorial/db6.md +0 -178
  72. docs/source/tutorial/index.md +0 -20
  73. docs/source/tutorial/mysetting.md +0 -124
  74. docs/source/tutorial/xml.md +0 -140
  75. pyrekordbox/xml.py +0 -8
  76. pyrekordbox-0.3.2.dist-info/RECORD +0 -84
  77. tests/__init__.py +0 -3
  78. tests/test_anlz.py +0 -206
  79. tests/test_config.py +0 -175
  80. tests/test_db6.py +0 -1193
  81. tests/test_mysetting.py +0 -203
  82. tests/test_xml.py +0 -629
  83. {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 sqlalchemy import Column, Integer, VARCHAR, BigInteger, SmallInteger, Text, Float
13
- from sqlalchemy import ForeignKey, TypeDecorator
14
- from sqlalchemy.orm import DeclarativeBase, relationship, backref, mapped_column, Mapped
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.isoformat().replace("T", " ")[:-3] + " +00:00"
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
- try:
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
- insp = inspect(self.__class__)
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
@@ -20,4 +20,3 @@ logger.addHandler(sh)
20
20
 
21
21
  # Set logging level
22
22
  logger.setLevel(logging.WARNING)
23
- logging.root.setLevel(logging.NOTSET)
@@ -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$")
@@ -5,8 +5,10 @@
5
5
  """Rekordbox My-Setting file handlers."""
6
6
 
7
7
  import re
8
- from construct import Struct
9
8
  from collections.abc import MutableMapping
9
+
10
+ from construct import Struct
11
+
10
12
  from . import structs
11
13
 
12
14
  # fmt: off
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
- import xml.etree.cElementTree as xml
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
  ----------