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.
Files changed (84) hide show
  1. pyrekordbox/__init__.py +8 -8
  2. pyrekordbox/__main__.py +3 -2
  3. pyrekordbox/_version.py +2 -2
  4. pyrekordbox/anlz/__init__.py +3 -2
  5. pyrekordbox/anlz/file.py +4 -2
  6. pyrekordbox/anlz/tags.py +3 -1
  7. pyrekordbox/config.py +79 -23
  8. pyrekordbox/db6/__init__.py +2 -2
  9. pyrekordbox/db6/aux_files.py +3 -2
  10. pyrekordbox/db6/database.py +227 -143
  11. pyrekordbox/db6/registry.py +1 -0
  12. pyrekordbox/db6/smartlist.py +375 -0
  13. pyrekordbox/db6/tables.py +81 -20
  14. pyrekordbox/logger.py +0 -1
  15. pyrekordbox/mysettings/__init__.py +5 -4
  16. pyrekordbox/mysettings/file.py +3 -1
  17. pyrekordbox/rbxml.py +5 -3
  18. pyrekordbox/utils.py +4 -3
  19. {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/LICENSE +1 -1
  20. {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/METADATA +26 -42
  21. pyrekordbox-0.4.0.dist-info/RECORD +25 -0
  22. {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/WHEEL +1 -1
  23. {pyrekordbox-0.3.1.dist-info → pyrekordbox-0.4.0.dist-info}/top_level.txt +0 -2
  24. docs/Makefile +0 -20
  25. docs/make.bat +0 -35
  26. docs/source/_static/images/anlz_beat.svg +0 -53
  27. docs/source/_static/images/anlz_file.svg +0 -204
  28. docs/source/_static/images/anlz_pco2.svg +0 -138
  29. docs/source/_static/images/anlz_pcob.svg +0 -148
  30. docs/source/_static/images/anlz_pcp2.svg +0 -398
  31. docs/source/_static/images/anlz_pcpt.svg +0 -263
  32. docs/source/_static/images/anlz_ppth.svg +0 -123
  33. docs/source/_static/images/anlz_pqt2.svg +0 -324
  34. docs/source/_static/images/anlz_pqt2_2.svg +0 -253
  35. docs/source/_static/images/anlz_pqtz.svg +0 -140
  36. docs/source/_static/images/anlz_pssi.svg +0 -192
  37. docs/source/_static/images/anlz_pssi_entry.svg +0 -191
  38. docs/source/_static/images/anlz_pvbr.svg +0 -125
  39. docs/source/_static/images/anlz_pwav.svg +0 -130
  40. docs/source/_static/images/anlz_pwv3.svg +0 -139
  41. docs/source/_static/images/anlz_pwv4.svg +0 -139
  42. docs/source/_static/images/anlz_pwv5.svg +0 -139
  43. docs/source/_static/images/anlz_pwv5_entry.svg +0 -100
  44. docs/source/_static/images/anlz_pwv6.svg +0 -130
  45. docs/source/_static/images/anlz_pwv7.svg +0 -139
  46. docs/source/_static/images/anlz_pwvc.svg +0 -125
  47. docs/source/_static/images/anlz_tag.svg +0 -110
  48. docs/source/_static/images/x64dbg_rb_key.png +0 -0
  49. docs/source/_static/logos/dark/logo_primary.svg +0 -75
  50. docs/source/_static/logos/light/logo_primary.svg +0 -75
  51. docs/source/_static/logos/mid/logo_primary.svg +0 -75
  52. docs/source/_templates/apidoc/module.rst_t +0 -8
  53. docs/source/_templates/apidoc/package.rst_t +0 -57
  54. docs/source/_templates/apidoc/toc.rst_t +0 -7
  55. docs/source/_templates/autosummary/class.rst +0 -32
  56. docs/source/_templates/autosummary/module.rst +0 -55
  57. docs/source/api.md +0 -18
  58. docs/source/conf.py +0 -178
  59. docs/source/development/changes.md +0 -3
  60. docs/source/development/contributing.md +0 -3
  61. docs/source/formats/anlz.md +0 -634
  62. docs/source/formats/db6.md +0 -1233
  63. docs/source/formats/mysetting.md +0 -392
  64. docs/source/formats/xml.md +0 -376
  65. docs/source/index.md +0 -103
  66. docs/source/installation.md +0 -271
  67. docs/source/key.md +0 -103
  68. docs/source/quickstart.md +0 -185
  69. docs/source/requirements.txt +0 -7
  70. docs/source/tutorial/anlz.md +0 -7
  71. docs/source/tutorial/configuration.md +0 -66
  72. docs/source/tutorial/db6.md +0 -178
  73. docs/source/tutorial/index.md +0 -20
  74. docs/source/tutorial/mysetting.md +0 -124
  75. docs/source/tutorial/xml.md +0 -140
  76. pyrekordbox/db6/smart_playlist.py +0 -333
  77. pyrekordbox/xml.py +0 -8
  78. pyrekordbox-0.3.1.dist-info/RECORD +0 -84
  79. tests/__init__.py +0 -3
  80. tests/test_anlz.py +0 -206
  81. tests/test_config.py +0 -175
  82. tests/test_db6.py +0 -1115
  83. tests/test_mysetting.py +0 -203
  84. 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 .db6 import Rekordbox6Database, open_rekordbox_database
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
- from pyrekordbox.config import write_db6_key_cache, _cache_file
11
+
12
+ from pyrekordbox.config import _cache_file, write_db6_key_cache
12
13
 
13
14
  KEY_SOURCES = [
14
15
  {
pyrekordbox/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.3.1'
16
- __version_tuple__ = version_tuple = (0, 3, 1)
15
+ __version__ = version = '0.4.0'
16
+ __version_tuple__ = version_tuple = (0, 4, 0)
@@ -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
- from .tags import TAGS
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) / "Pioneer"
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 == 6 else "rekordbox"
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 == 6 else "datafile.edb"
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"]["dp"] = key
467
+ if __config__["rekordbox6"]:
468
+ __config__["rekordbox6"]["dp"] = key
469
+ if __config__["rekordbox7"]:
470
+ __config__["rekordbox7"]["dp"] = key
461
471
 
462
472
 
463
- def _get_rb6_config(
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
- executable = (
524
- conf["install_dir"] / "Contents" / "MacOS" / "rekordbox"
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
 
@@ -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
@@ -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