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
pyrekordbox/__init__.py
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
# Author: Dylan Jones
|
3
3
|
# Date: 2022-04-10
|
4
4
|
|
5
|
+
from .anlz import AnlzFile, get_anlz_paths, read_anlz_files, walk_anlz_paths
|
6
|
+
from .config import get_config, show_config, update_config
|
7
|
+
from .db6 import Rekordbox6Database
|
5
8
|
from .logger import logger
|
6
|
-
from .config import show_config, get_config, update_config
|
7
|
-
from .rbxml import RekordboxXml, XmlDuplicateError, XmlAttributeKeyError
|
8
|
-
from .anlz import get_anlz_paths, walk_anlz_paths, read_anlz_files, AnlzFile
|
9
9
|
from .mysettings import (
|
10
|
+
DevSettingFile,
|
11
|
+
DjmMySettingFile,
|
12
|
+
MySetting2File,
|
13
|
+
MySettingFile,
|
10
14
|
get_mysetting_paths,
|
11
15
|
read_mysetting_file,
|
12
|
-
MySettingFile,
|
13
|
-
MySetting2File,
|
14
|
-
DjmMySettingFile,
|
15
|
-
DevSettingFile,
|
16
16
|
)
|
17
|
-
from .
|
17
|
+
from .rbxml import RekordboxXml, XmlAttributeKeyError, XmlDuplicateError
|
18
18
|
|
19
19
|
try:
|
20
20
|
from ._version import version as __version__
|
pyrekordbox/__main__.py
CHANGED
@@ -4,11 +4,12 @@
|
|
4
4
|
|
5
5
|
import os
|
6
6
|
import re
|
7
|
-
import sys
|
8
7
|
import shutil
|
8
|
+
import sys
|
9
9
|
import urllib.request
|
10
10
|
from pathlib import Path
|
11
|
-
|
11
|
+
|
12
|
+
from pyrekordbox.config import _cache_file, write_db6_key_cache
|
12
13
|
|
13
14
|
KEY_SOURCES = [
|
14
15
|
{
|
pyrekordbox/_version.py
CHANGED
pyrekordbox/anlz/__init__.py
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
import re
|
6
6
|
from pathlib import Path
|
7
7
|
from typing import Union
|
8
|
+
|
8
9
|
from . import structs
|
9
10
|
from .file import AnlzFile
|
10
11
|
|
@@ -77,7 +78,7 @@ def walk_anlz_dirs(root_dir: Union[str, Path]):
|
|
77
78
|
The path of the root directory.
|
78
79
|
|
79
80
|
Yields
|
80
|
-
|
81
|
+
------
|
81
82
|
anlz_dir : str
|
82
83
|
The path of a directory containing ANLZ files
|
83
84
|
"""
|
@@ -96,7 +97,7 @@ def walk_anlz_paths(root_dir: Union[str, Path]):
|
|
96
97
|
The path of the root directory.
|
97
98
|
|
98
99
|
Yields
|
99
|
-
|
100
|
+
------
|
100
101
|
anlz_dir : str
|
101
102
|
The path of a directory containing ANLZ files.
|
102
103
|
anlz_files : Sequence of str
|
pyrekordbox/anlz/file.py
CHANGED
@@ -6,10 +6,12 @@ import logging
|
|
6
6
|
from collections import abc
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import Union
|
9
|
-
|
10
|
-
from . import structs
|
9
|
+
|
11
10
|
from construct import Int16ub
|
12
11
|
|
12
|
+
from . import structs
|
13
|
+
from .tags import TAGS
|
14
|
+
|
13
15
|
logger = logging.getLogger(__name__)
|
14
16
|
|
15
17
|
XOR_MASK = bytearray.fromhex("CB E1 EE FA E5 EE AD EE E9 D2 E9 EB E1 E9 F3 E8 E9 F4 E1")
|
pyrekordbox/anlz/tags.py
CHANGED
@@ -4,7 +4,9 @@
|
|
4
4
|
|
5
5
|
import logging
|
6
6
|
from abc import ABC
|
7
|
+
|
7
8
|
import numpy as np
|
9
|
+
|
8
10
|
from . import structs
|
9
11
|
|
10
12
|
logger = logging.getLogger(__name__)
|
@@ -439,7 +441,7 @@ class PWV5AnlzTag(AbstractAnlzTag):
|
|
439
441
|
LEN_HEADER = 24
|
440
442
|
|
441
443
|
def get(self):
|
442
|
-
"""Parse the Waveform Color Detail Tag (PWV5)
|
444
|
+
"""Parse the Waveform Color Detail Tag (PWV5).
|
443
445
|
|
444
446
|
The format of the entries is:
|
445
447
|
|
pyrekordbox/config.py
CHANGED
@@ -40,6 +40,7 @@ __config__ = {
|
|
40
40
|
},
|
41
41
|
"rekordbox5": {},
|
42
42
|
"rekordbox6": {},
|
43
|
+
"rekordbox7": {},
|
43
44
|
}
|
44
45
|
|
45
46
|
|
@@ -50,7 +51,8 @@ class InvalidApplicationDirname(Exception):
|
|
50
51
|
def get_pioneer_install_dir(path: Union[str, Path] = None) -> Path: # pragma: no cover
|
51
52
|
"""Returns the path of the Pioneer program installation directory.
|
52
53
|
|
53
|
-
On Windows, the Pioneer program data is stored in `/ProgramFiles/Pioneer
|
54
|
+
On Windows, the Pioneer program data is stored in `/ProgramFiles/Pioneer`.
|
55
|
+
For rekordbox version 7 this has changed to `/ProgramFiles/rekordbox`.
|
54
56
|
On macOS the program data is somewhere in `/Applications/`.
|
55
57
|
|
56
58
|
Parameters
|
@@ -68,7 +70,7 @@ def get_pioneer_install_dir(path: Union[str, Path] = None) -> Path: # pragma: n
|
|
68
70
|
if sys.platform == "win32":
|
69
71
|
# Windows: located in /ProgramFiles/Pioneer
|
70
72
|
program_files = os.environ["ProgramFiles"].replace("(x86)", "").strip()
|
71
|
-
path = Path(program_files)
|
73
|
+
path = Path(program_files)
|
72
74
|
elif sys.platform == "darwin":
|
73
75
|
# MacOS: located in /Applications/
|
74
76
|
path = Path("/Applications")
|
@@ -284,6 +286,12 @@ def _get_rb_config(
|
|
284
286
|
config : dict
|
285
287
|
The program configuration.
|
286
288
|
"""
|
289
|
+
if sys.platform == "win32":
|
290
|
+
if major_version >= 7:
|
291
|
+
pioneer_install_dir = pioneer_install_dir / "rekordbox"
|
292
|
+
else:
|
293
|
+
pioneer_install_dir = pioneer_install_dir / "Pioneer"
|
294
|
+
|
287
295
|
if application_dirname:
|
288
296
|
# Applitcation dirname is given, only extract version from it
|
289
297
|
# `major_version` is compared to the version string
|
@@ -328,7 +336,7 @@ def _get_rb_config(
|
|
328
336
|
logger.debug("Found Rekordbox %s install-dir: '%s'", major_version, rb_prog_dir)
|
329
337
|
|
330
338
|
# Get Rekordbox application directory path for major release `major_version`
|
331
|
-
name = "rekordbox6" if major_version
|
339
|
+
name = "rekordbox6" if major_version >= 6 else "rekordbox"
|
332
340
|
rb_app_dir = pioneer_app_dir / name
|
333
341
|
if not rb_app_dir.exists():
|
334
342
|
raise FileNotFoundError(f"The directory '{rb_app_dir}' doesn't exist!")
|
@@ -337,7 +345,7 @@ def _get_rb_config(
|
|
337
345
|
# Get Rekordbox database locations for major release `major_version`
|
338
346
|
settings = read_rekordbox_settings(rb_app_dir)
|
339
347
|
db_dir = Path(settings["masterDbDirectory"])
|
340
|
-
db_filename = "master.db" if major_version
|
348
|
+
db_filename = "master.db" if major_version >= 6 else "datafile.edb"
|
341
349
|
db_path = db_dir / db_filename
|
342
350
|
if not db_path.exists():
|
343
351
|
raise FileNotFoundError(f"The Rekordbox database '{db_path}' doesn't exist!")
|
@@ -363,7 +371,6 @@ def _get_rb5_config(
|
|
363
371
|
|
364
372
|
def _extract_pw(pioneer_install_dir: Path) -> str: # pragma: no cover
|
365
373
|
"""Extract the password for decrypting the Rekordbox 6 database key."""
|
366
|
-
|
367
374
|
asar_data = read_rekordbox6_asar(pioneer_install_dir)
|
368
375
|
match_result = re.search('pass: ".(.*?)"', asar_data)
|
369
376
|
if match_result is None:
|
@@ -457,23 +464,13 @@ def write_db6_key_cache(key: str) -> None: # pragma: no cover
|
|
457
464
|
with open(_cache_file, "w") as fh:
|
458
465
|
fh.write(text)
|
459
466
|
# Set the config key to make sure the key is present after calling method
|
460
|
-
__config__["rekordbox6"]
|
467
|
+
if __config__["rekordbox6"]:
|
468
|
+
__config__["rekordbox6"]["dp"] = key
|
469
|
+
if __config__["rekordbox7"]:
|
470
|
+
__config__["rekordbox7"]["dp"] = key
|
461
471
|
|
462
472
|
|
463
|
-
def
|
464
|
-
pioneer_prog_dir: Path, pioneer_app_dir: Path, dirname: str = ""
|
465
|
-
) -> dict:
|
466
|
-
"""Get the program configuration for Rekordbox v6.x.x."""
|
467
|
-
major_version = 6
|
468
|
-
conf = _get_rb_config(pioneer_prog_dir, pioneer_app_dir, major_version, dirname)
|
469
|
-
|
470
|
-
# Read Rekordbox 6 'options.json' and check db_path
|
471
|
-
opts = read_rekordbox6_options(pioneer_app_dir)
|
472
|
-
db_path = Path(opts["db-path"])
|
473
|
-
db_dir = db_path.parent
|
474
|
-
assert str(conf["db_dir"]) == str(db_dir)
|
475
|
-
assert str(conf["db_path"]) == str(db_path)
|
476
|
-
|
473
|
+
def _update_sqlite_key(opts, conf):
|
477
474
|
cache_version = 0
|
478
475
|
pw, dp = "", ""
|
479
476
|
if _cache_file.exists(): # pragma: no cover
|
@@ -520,9 +517,10 @@ def _get_rb6_config(
|
|
520
517
|
if sys.platform == "win32":
|
521
518
|
executable = conf["install_dir"] / "rekordbox.exe"
|
522
519
|
elif sys.platform == "darwin":
|
523
|
-
|
524
|
-
|
525
|
-
|
520
|
+
install_dir = conf["install_dir"]
|
521
|
+
if not str(install_dir).endswith(".app"):
|
522
|
+
install_dir = install_dir / "rekordbox.app"
|
523
|
+
executable = install_dir / "Contents" / "MacOS" / "rekordbox"
|
526
524
|
else:
|
527
525
|
# Linux: not supported
|
528
526
|
logger.warning(f"OS {sys.platform} not supported!")
|
@@ -552,6 +550,44 @@ def _get_rb6_config(
|
|
552
550
|
if dp:
|
553
551
|
conf["dp"] = dp
|
554
552
|
|
553
|
+
|
554
|
+
def _get_rb6_config(
|
555
|
+
pioneer_prog_dir: Path, pioneer_app_dir: Path, dirname: str = ""
|
556
|
+
) -> dict:
|
557
|
+
"""Get the program configuration for Rekordbox v6.x.x."""
|
558
|
+
major_version = 6
|
559
|
+
conf = _get_rb_config(pioneer_prog_dir, pioneer_app_dir, major_version, dirname)
|
560
|
+
|
561
|
+
# Read Rekordbox 6 'options.json' and check db_path
|
562
|
+
opts = read_rekordbox6_options(pioneer_app_dir)
|
563
|
+
db_path = Path(opts["db-path"])
|
564
|
+
db_dir = db_path.parent
|
565
|
+
assert str(conf["db_dir"]) == str(db_dir)
|
566
|
+
assert str(conf["db_path"]) == str(db_path)
|
567
|
+
|
568
|
+
# Update SQLite key
|
569
|
+
_update_sqlite_key(opts, conf)
|
570
|
+
|
571
|
+
return conf
|
572
|
+
|
573
|
+
|
574
|
+
def _get_rb7_config(
|
575
|
+
pioneer_prog_dir: Path, pioneer_app_dir: Path, dirname: str = ""
|
576
|
+
) -> dict:
|
577
|
+
"""Get the program configuration for Rekordbox v7.x.x."""
|
578
|
+
major_version = 7
|
579
|
+
conf = _get_rb_config(pioneer_prog_dir, pioneer_app_dir, major_version, dirname)
|
580
|
+
|
581
|
+
# Read Rekordbox 6 'options.json' and check db_path
|
582
|
+
opts = read_rekordbox6_options(pioneer_app_dir)
|
583
|
+
db_path = Path(opts["db-path"])
|
584
|
+
db_dir = db_path.parent
|
585
|
+
assert str(conf["db_dir"]) == str(db_dir)
|
586
|
+
assert str(conf["db_path"]) == str(db_path)
|
587
|
+
|
588
|
+
# Update SQLite key
|
589
|
+
_update_sqlite_key(opts, conf)
|
590
|
+
|
555
591
|
return conf
|
556
592
|
|
557
593
|
|
@@ -617,6 +653,7 @@ def update_config(
|
|
617
653
|
pioneer_app_dir: Union[str, Path] = None,
|
618
654
|
rb5_install_dirname: str = "",
|
619
655
|
rb6_install_dirname: str = "",
|
656
|
+
rb7_install_dirname: str = "",
|
620
657
|
):
|
621
658
|
"""Update the pyrekordbox configuration.
|
622
659
|
|
@@ -643,6 +680,9 @@ def update_config(
|
|
643
680
|
rb6_install_dirname : str, optional
|
644
681
|
The name of the Rekordbox 6 installation directory. By default, the normal
|
645
682
|
directory name is used (Windows: 'rekordbox 6.x.x', macOS: 'rekordbox 6.app').
|
683
|
+
rb7_install_dirname : str, optional
|
684
|
+
The name of the Rekordbox 7 installation directory. By default, the normal
|
685
|
+
directory name is used (Windows: 'rekordbox 7.x.x', macOS: 'rekordbox 7.app').
|
646
686
|
"""
|
647
687
|
# Read config file
|
648
688
|
conf = read_pyrekordbox_configuration()
|
@@ -654,6 +694,8 @@ def update_config(
|
|
654
694
|
rb5_install_dirname = conf["rekordbox5-install-dirname"]
|
655
695
|
if not rb6_install_dirname and "rekordbox6-install-dirname" in conf:
|
656
696
|
rb6_install_dirname = conf["rekordbox6-install-dirname"]
|
697
|
+
if not rb7_install_dirname and "rekordbox7-install-dirname" in conf:
|
698
|
+
rb7_install_dirname = conf["rekordbox7-install-dirname"]
|
657
699
|
|
658
700
|
# Pioneer installation directory
|
659
701
|
try:
|
@@ -689,6 +731,15 @@ def update_config(
|
|
689
731
|
except FileNotFoundError as e:
|
690
732
|
logger.info(e)
|
691
733
|
|
734
|
+
# Update Rekordbox 7 config
|
735
|
+
try:
|
736
|
+
conf = _get_rb7_config(
|
737
|
+
pioneer_install_dir, pioneer_app_dir, rb7_install_dirname
|
738
|
+
)
|
739
|
+
__config__["rekordbox7"].update(conf)
|
740
|
+
except FileNotFoundError as e:
|
741
|
+
logger.info(e)
|
742
|
+
|
692
743
|
|
693
744
|
def get_config(section: str, key: str = None):
|
694
745
|
"""Gets a section or value of the pyrekordbox configuration.
|
@@ -721,6 +772,7 @@ def pformat_config(indent: str = " ", hw: int = 14, delim: str = " = ") -> str
|
|
721
772
|
pioneer = get_config("pioneer")
|
722
773
|
rb5 = get_config("rekordbox5")
|
723
774
|
rb6 = get_config("rekordbox6")
|
775
|
+
rb7 = get_config("rekordbox7")
|
724
776
|
|
725
777
|
lines = ["Pioneer:"]
|
726
778
|
lines += [f"{indent}{k + delim:<{hw}} {pioneer[k]}" for k in sorted(pioneer.keys())]
|
@@ -731,6 +783,10 @@ def pformat_config(indent: str = " ", hw: int = 14, delim: str = " = ") -> str
|
|
731
783
|
if rb6:
|
732
784
|
rb6_keys = [k for k in rb6.keys() if k not in ("dp", "p")]
|
733
785
|
lines += [f"{indent}{k + delim:<{hw}} {rb6[k]}" for k in sorted(rb6_keys)]
|
786
|
+
lines.append("Rekordbox 7:")
|
787
|
+
if rb7:
|
788
|
+
rb7_keys = [k for k in rb7.keys() if k not in ("dp", "p")]
|
789
|
+
lines += [f"{indent}{k + delim:<{hw}} {rb7[k]}" for k in sorted(rb7_keys)]
|
734
790
|
return "\n".join(lines)
|
735
791
|
|
736
792
|
|
pyrekordbox/db6/__init__.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
# Author: Dylan Jones
|
3
3
|
# Date: 2022-05-07
|
4
4
|
|
5
|
+
from .database import Rekordbox6Database
|
6
|
+
from .smartlist import SmartList
|
5
7
|
from .tables import (
|
6
8
|
AgentRegistry,
|
7
9
|
CloudAgentRegistry,
|
@@ -41,5 +43,3 @@ from .tables import (
|
|
41
43
|
SettingFile,
|
42
44
|
UuidIDMap,
|
43
45
|
)
|
44
|
-
from .smart_playlist import SmartList
|
45
|
-
from .database import Rekordbox6Database, open_rekordbox_database
|
pyrekordbox/db6/aux_files.py
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
# Author: Dylan Jones
|
3
3
|
# Date: 2023-09-10
|
4
4
|
|
5
|
-
from pathlib import Path
|
6
|
-
from datetime import datetime
|
7
5
|
import xml.etree.cElementTree as xml
|
6
|
+
from datetime import datetime
|
7
|
+
from pathlib import Path
|
8
|
+
|
8
9
|
from ..config import get_config
|
9
10
|
from ..utils import pretty_xml
|
10
11
|
|