pyrekordbox 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. docs/source/formats/anlz.md +178 -7
  2. docs/source/formats/db6.md +1 -1
  3. docs/source/index.md +2 -6
  4. docs/source/quickstart.md +68 -45
  5. docs/source/tutorial/index.md +1 -1
  6. pyrekordbox/__init__.py +1 -1
  7. pyrekordbox/_version.py +2 -2
  8. pyrekordbox/anlz/file.py +39 -0
  9. pyrekordbox/anlz/structs.py +3 -5
  10. pyrekordbox/config.py +71 -27
  11. pyrekordbox/db6/database.py +260 -33
  12. pyrekordbox/db6/registry.py +22 -0
  13. pyrekordbox/db6/tables.py +3 -4
  14. {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/METADATA +12 -11
  15. pyrekordbox-0.2.2.dist-info/RECORD +80 -0
  16. {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/top_level.txt +0 -2
  17. tests/test_config.py +175 -0
  18. tests/test_db6.py +78 -0
  19. build/lib/build/lib/docs/source/conf.py +0 -178
  20. build/lib/build/lib/pyrekordbox/__init__.py +0 -22
  21. build/lib/build/lib/pyrekordbox/__main__.py +0 -204
  22. build/lib/build/lib/pyrekordbox/_version.py +0 -16
  23. build/lib/build/lib/pyrekordbox/anlz/__init__.py +0 -127
  24. build/lib/build/lib/pyrekordbox/anlz/file.py +0 -186
  25. build/lib/build/lib/pyrekordbox/anlz/structs.py +0 -299
  26. build/lib/build/lib/pyrekordbox/anlz/tags.py +0 -508
  27. build/lib/build/lib/pyrekordbox/config.py +0 -596
  28. build/lib/build/lib/pyrekordbox/db6/__init__.py +0 -45
  29. build/lib/build/lib/pyrekordbox/db6/aux_files.py +0 -213
  30. build/lib/build/lib/pyrekordbox/db6/database.py +0 -1808
  31. build/lib/build/lib/pyrekordbox/db6/registry.py +0 -304
  32. build/lib/build/lib/pyrekordbox/db6/tables.py +0 -1618
  33. build/lib/build/lib/pyrekordbox/logger.py +0 -23
  34. build/lib/build/lib/pyrekordbox/mysettings/__init__.py +0 -32
  35. build/lib/build/lib/pyrekordbox/mysettings/file.py +0 -369
  36. build/lib/build/lib/pyrekordbox/mysettings/structs.py +0 -282
  37. build/lib/build/lib/pyrekordbox/utils.py +0 -162
  38. build/lib/build/lib/pyrekordbox/xml.py +0 -1294
  39. build/lib/build/lib/tests/__init__.py +0 -3
  40. build/lib/build/lib/tests/test_anlz.py +0 -206
  41. build/lib/build/lib/tests/test_db6.py +0 -1039
  42. build/lib/build/lib/tests/test_mysetting.py +0 -203
  43. build/lib/build/lib/tests/test_xml.py +0 -629
  44. build/lib/docs/source/conf.py +0 -178
  45. build/lib/pyrekordbox/__init__.py +0 -22
  46. build/lib/pyrekordbox/__main__.py +0 -204
  47. build/lib/pyrekordbox/_version.py +0 -16
  48. build/lib/pyrekordbox/anlz/__init__.py +0 -127
  49. build/lib/pyrekordbox/anlz/file.py +0 -186
  50. build/lib/pyrekordbox/anlz/structs.py +0 -299
  51. build/lib/pyrekordbox/anlz/tags.py +0 -508
  52. build/lib/pyrekordbox/config.py +0 -596
  53. build/lib/pyrekordbox/db6/__init__.py +0 -45
  54. build/lib/pyrekordbox/db6/aux_files.py +0 -213
  55. build/lib/pyrekordbox/db6/database.py +0 -1808
  56. build/lib/pyrekordbox/db6/registry.py +0 -304
  57. build/lib/pyrekordbox/db6/tables.py +0 -1618
  58. build/lib/pyrekordbox/logger.py +0 -23
  59. build/lib/pyrekordbox/mysettings/__init__.py +0 -32
  60. build/lib/pyrekordbox/mysettings/file.py +0 -369
  61. build/lib/pyrekordbox/mysettings/structs.py +0 -282
  62. build/lib/pyrekordbox/utils.py +0 -162
  63. build/lib/pyrekordbox/xml.py +0 -1294
  64. build/lib/tests/__init__.py +0 -3
  65. build/lib/tests/test_anlz.py +0 -206
  66. build/lib/tests/test_db6.py +0 -1039
  67. build/lib/tests/test_mysetting.py +0 -203
  68. build/lib/tests/test_xml.py +0 -629
  69. pyrekordbox-0.2.1.dist-info/RECORD +0 -129
  70. {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/LICENSE +0 -0
  71. {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/WHEEL +0 -0
tests/test_config.py ADDED
@@ -0,0 +1,175 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Author: Dylan Jones
3
+ # Date: 2023-10-02
4
+
5
+ import pytest
6
+ import json
7
+ from pathlib import Path
8
+ from pyrekordbox.config import update_config, get_config
9
+
10
+ RB_SETTING = """<?xml version="1.0" encoding="UTF-8"?>
11
+ <PROPERTIES><VALUE name="masterDbDirectory" val="{db_dir}"/></PROPERTIES>
12
+ """
13
+
14
+ RB_OPTIONS = {"options": [["db-path", ""]]}
15
+
16
+
17
+ def mock_rekordbox_settings_file(pioneer_app_dir, rb_app_dir):
18
+ db_dir = pioneer_app_dir / "rekordbox"
19
+ text = RB_SETTING.format(db_dir=db_dir)
20
+ file = rb_app_dir / "rekordbox3.settings"
21
+ file.write_text(text)
22
+
23
+
24
+ def mock_rekordbox_options_file(agent_storage_dir, db_path):
25
+ file = agent_storage_dir / "options.json"
26
+ options = RB_OPTIONS.copy()
27
+ options["options"][0][1] = str(db_path)
28
+ with open(file, "w") as fp:
29
+ json.dump(options, fp)
30
+
31
+
32
+ @pytest.fixture(scope="session")
33
+ def pioneer_app_dir(tmp_path_factory):
34
+ root = tmp_path_factory.mktemp("Pioneer")
35
+ rb_dir = root / "rekordbox"
36
+ rb6_dir = root / "rekordbox6"
37
+ agent_storage_dir = root / "rekordboxAgent" / "storage"
38
+ rb_dir.mkdir(parents=True)
39
+ rb6_dir.mkdir(parents=True)
40
+ agent_storage_dir.mkdir(parents=True)
41
+
42
+ # Mock `rekordbox3.settings` files
43
+ mock_rekordbox_settings_file(root, rb_dir)
44
+ mock_rekordbox_settings_file(root, rb6_dir)
45
+
46
+ # Mock database files
47
+ rb5_db = rb_dir / "datafile.edb"
48
+ rb6_db = rb_dir / "master.db"
49
+ rb5_db.touch()
50
+ rb6_db.touch()
51
+
52
+ # Mock `options.json` file (RBv6)
53
+ mock_rekordbox_options_file(agent_storage_dir, rb6_db)
54
+
55
+ return root
56
+
57
+
58
+ @pytest.fixture(scope="session")
59
+ def pioneer_install_dir(tmp_path_factory):
60
+ root = tmp_path_factory.mktemp("Pioneer")
61
+ rb5_dir = root / "rekordbox 5.1.1"
62
+ rb6_dir = root / "rekordbox 6.1.1"
63
+ rb5_dir_alt = root / "rekordbox 5.1.2"
64
+ rb6_dir_alt = root / "rekordbox 6.1.2"
65
+
66
+ rb5_dir.mkdir(parents=True)
67
+ rb6_dir.mkdir(parents=True)
68
+ rb5_dir_alt.mkdir(parents=True)
69
+ rb6_dir_alt.mkdir(parents=True)
70
+ return root
71
+
72
+
73
+ def test_pioneer_config(pioneer_install_dir, pioneer_app_dir):
74
+ update_config(pioneer_install_dir, pioneer_app_dir)
75
+ install_dir = get_config("pioneer", "install_dir")
76
+ app_dir = get_config("pioneer", "app_dir")
77
+
78
+ assert isinstance(install_dir, Path)
79
+ assert isinstance(app_dir, Path)
80
+ assert install_dir == pioneer_install_dir
81
+ assert app_dir == pioneer_app_dir
82
+
83
+
84
+ def test_rb5_config(pioneer_install_dir, pioneer_app_dir):
85
+ update_config(pioneer_install_dir, pioneer_app_dir)
86
+ expected_version = "5.1.2"
87
+
88
+ app_dir = get_config("rekordbox5", "app_dir")
89
+ install_dir = get_config("rekordbox5", "install_dir")
90
+ db_dir = get_config("rekordbox5", "db_dir")
91
+ db_path = get_config("rekordbox5", "db_path")
92
+ version = get_config("rekordbox5", "version")
93
+
94
+ assert isinstance(install_dir, Path)
95
+ assert isinstance(app_dir, Path)
96
+ assert isinstance(db_dir, Path)
97
+ assert isinstance(db_path, Path)
98
+ assert install_dir == (pioneer_install_dir / f"rekordbox {expected_version}")
99
+ assert app_dir == (pioneer_app_dir / "rekordbox")
100
+ assert db_dir == (pioneer_app_dir / "rekordbox")
101
+ assert db_path == (pioneer_app_dir / "rekordbox" / "datafile.edb")
102
+ assert version == expected_version
103
+
104
+
105
+ def test_rb6_config(pioneer_install_dir, pioneer_app_dir):
106
+ update_config(pioneer_install_dir, pioneer_app_dir)
107
+ expected_version = "6.1.2"
108
+
109
+ app_dir = get_config("rekordbox6", "app_dir")
110
+ install_dir = get_config("rekordbox6", "install_dir")
111
+ db_dir = get_config("rekordbox6", "db_dir")
112
+ db_path = get_config("rekordbox6", "db_path")
113
+ version = get_config("rekordbox6", "version")
114
+
115
+ assert isinstance(install_dir, Path)
116
+ assert isinstance(app_dir, Path)
117
+ assert isinstance(db_dir, Path)
118
+ assert isinstance(db_path, Path)
119
+ assert install_dir == (pioneer_install_dir / f"rekordbox {expected_version}")
120
+ assert app_dir == (pioneer_app_dir / "rekordbox6")
121
+ assert db_dir == (pioneer_app_dir / "rekordbox")
122
+ assert db_path == (pioneer_app_dir / "rekordbox" / "master.db")
123
+ assert version == expected_version
124
+
125
+
126
+ def test_rb5_config_alt(pioneer_install_dir, pioneer_app_dir):
127
+ # test default: latest version
128
+ update_config(pioneer_install_dir, pioneer_app_dir)
129
+ expected_version = "5.1.2"
130
+
131
+ app_dir = get_config("rekordbox5", "app_dir")
132
+ install_dir = get_config("rekordbox5", "install_dir")
133
+ version = get_config("rekordbox5", "version")
134
+ assert install_dir == (pioneer_install_dir / f"rekordbox {expected_version}")
135
+ assert app_dir == (pioneer_app_dir / "rekordbox")
136
+ assert version == expected_version
137
+
138
+ # test alternative version
139
+ update_config(
140
+ pioneer_install_dir, pioneer_app_dir, rb5_install_dirname="rekordbox 5.1.1"
141
+ )
142
+ expected_version = "5.1.1"
143
+
144
+ app_dir = get_config("rekordbox5", "app_dir")
145
+ install_dir = get_config("rekordbox5", "install_dir")
146
+ version = get_config("rekordbox5", "version")
147
+ assert install_dir == (pioneer_install_dir / f"rekordbox {expected_version}")
148
+ assert app_dir == (pioneer_app_dir / "rekordbox")
149
+ assert version == expected_version
150
+
151
+
152
+ def test_rb6_config_alt(pioneer_install_dir, pioneer_app_dir):
153
+ # test default: latest version
154
+ update_config(pioneer_install_dir, pioneer_app_dir)
155
+ expected_version = "6.1.2"
156
+
157
+ app_dir = get_config("rekordbox6", "app_dir")
158
+ install_dir = get_config("rekordbox6", "install_dir")
159
+ version = get_config("rekordbox6", "version")
160
+ assert install_dir == (pioneer_install_dir / f"rekordbox {expected_version}")
161
+ assert app_dir == (pioneer_app_dir / "rekordbox6")
162
+ assert version == expected_version
163
+
164
+ # test alternative version
165
+ update_config(
166
+ pioneer_install_dir, pioneer_app_dir, rb6_install_dirname="rekordbox 6.1.1"
167
+ )
168
+ expected_version = "6.1.1"
169
+
170
+ app_dir = get_config("rekordbox6", "app_dir")
171
+ install_dir = get_config("rekordbox6", "install_dir")
172
+ version = get_config("rekordbox6", "version")
173
+ assert install_dir == (pioneer_install_dir / f"rekordbox {expected_version}")
174
+ assert app_dir == (pioneer_app_dir / "rekordbox6")
175
+ assert version == expected_version
tests/test_db6.py CHANGED
@@ -998,6 +998,84 @@ def test_rename_playlist(db):
998
998
  assert _check_playlist_xml(db)
999
999
 
1000
1000
 
1001
+ def test_add_album(db):
1002
+ old_usn = db.get_local_usn()
1003
+ name = "test"
1004
+ db.add_album(name)
1005
+ db.commit()
1006
+
1007
+ # Check that album was created and USN is incremented
1008
+ instance = db.get_album(Name=name).one()
1009
+ assert instance.Name == name
1010
+ assert instance.rb_local_usn == old_usn + 1
1011
+ assert db.get_local_usn() == old_usn + 1
1012
+
1013
+ # Fail if album with same name is added
1014
+ with pytest.raises(ValueError):
1015
+ db.add_album(name)
1016
+
1017
+ # Add album with album artist by artist
1018
+ artist = db.get_artist().first()
1019
+ album = db.add_album("album 2", artist=artist)
1020
+ assert album.AlbumArtistID == artist.ID
1021
+
1022
+ # Add album with album artist by ID
1023
+ artist = db.get_artist().first()
1024
+ album = db.add_album("album 3", artist=artist.ID)
1025
+ assert album.AlbumArtistID == artist.ID
1026
+
1027
+
1028
+ def test_add_artist(db):
1029
+ old_usn = db.get_local_usn()
1030
+ name = "test"
1031
+ db.add_artist(name)
1032
+ db.commit()
1033
+
1034
+ # Check that album was created and USN is incremented
1035
+ instance = db.get_artist(Name=name).one()
1036
+ assert instance.Name == name
1037
+ assert instance.rb_local_usn == old_usn + 1
1038
+ assert db.get_local_usn() == old_usn + 1
1039
+
1040
+ # Fail if album with same name is added
1041
+ with pytest.raises(ValueError):
1042
+ db.add_artist(name)
1043
+
1044
+
1045
+ def test_add_genre(db):
1046
+ old_usn = db.get_local_usn()
1047
+ name = "test"
1048
+ db.add_genre(name)
1049
+ db.commit()
1050
+
1051
+ # Check that album was created and USN is incremented
1052
+ instance = db.get_genre(Name=name).one()
1053
+ assert instance.Name == name
1054
+ assert instance.rb_local_usn == old_usn + 1
1055
+ assert db.get_local_usn() == old_usn + 1
1056
+
1057
+ # Fail if album with same name is added
1058
+ with pytest.raises(ValueError):
1059
+ db.add_genre(name)
1060
+
1061
+
1062
+ def test_add_label(db):
1063
+ old_usn = db.get_local_usn()
1064
+ name = "test"
1065
+ db.add_label(name)
1066
+ db.commit()
1067
+
1068
+ # Check that album was created and USN is incremented
1069
+ instance = db.get_label(Name=name).one()
1070
+ assert instance.Name == name
1071
+ assert instance.rb_local_usn == old_usn + 1
1072
+ assert db.get_local_usn() == old_usn + 1
1073
+
1074
+ # Fail if album with same name is added
1075
+ with pytest.raises(ValueError):
1076
+ db.add_label(name)
1077
+
1078
+
1001
1079
  def test_get_anlz_paths():
1002
1080
  content = DB.get_content().first()
1003
1081
 
@@ -1,178 +0,0 @@
1
- # Configuration file for the Sphinx documentation builder.
2
- #
3
- # This file only contains a selection of the most common options. For a full
4
- # list see the documentation:
5
- # https://www.sphinx-doc.org/en/master/usage/configuration.html
6
-
7
- # -- Path setup --------------------------------------------------------------
8
-
9
- # If extensions (or modules to document with autodoc) are in another directory,
10
- # add these directories to sys.path here. If the directory is relative to the
11
- # documentation root, use os.path.abspath to make it absolute, like shown here.
12
- #
13
- import os
14
- import sys
15
-
16
- sys.path.insert(0, os.path.abspath("../.."))
17
-
18
- import pyrekordbox
19
-
20
-
21
- # -- Project information -----------------------------------------------------
22
-
23
- project = "pyrekordbox"
24
- copyright = "2022-2023, Dylan Jones"
25
- author = "Dylan L. Jones"
26
- release = pyrekordbox.__version__
27
- version = release
28
- for sp in "abcfr":
29
- version = version.split(sp)[0]
30
-
31
- # -- General configuration ---------------------------------------------------
32
-
33
- # If your documentation needs a minimal Sphinx version, state it here.
34
- needs_sphinx = "4.4"
35
-
36
- # Add any Sphinx extension module names here, as strings. They can be
37
- # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
38
- # ones.
39
- extensions = [
40
- "sphinx.ext.autodoc",
41
- "numpydoc",
42
- "myst_parser",
43
- "numpydoc",
44
- "sphinx_copybutton",
45
- "sphinx.ext.napoleon",
46
- "sphinx.ext.autosummary",
47
- "sphinx.ext.autosectionlabel",
48
- "matplotlib.sphinxext.plot_directive",
49
- "sphinx.ext.intersphinx", # links to numpy, scipy ... docs
50
- "sphinx.ext.coverage",
51
- "sphinx.ext.extlinks", # define roles for links
52
- "sphinx.ext.viewcode",
53
- ]
54
-
55
- # If you need extensions of a certain version or higher, list them here.
56
- needs_extensions = {"myst_parser": "0.13.7"}
57
-
58
- # Add any paths that contain templates here, relative to this directory.
59
- templates_path = ["_templates"]
60
-
61
- # List of patterns, relative to source directory, that match files and
62
- # directories to ignore when looking for source files.
63
- # This pattern also affects html_static_path and html_extra_path.
64
-
65
- exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "tests"]
66
-
67
- # The suffix(es) of source filenames.
68
- # You can specify multiple suffix as a list of string:
69
- source_suffix = [".rst", ".md"]
70
-
71
- # Add any paths that contain custom static files (such as style sheets) here,
72
- # relative to this directory. They are copied after the builtin static files,
73
- # so a file named "default.css" will overwrite the builtin "default.css".
74
- html_static_path = ["_static"]
75
-
76
- html_theme_options = {
77
- "light_logo": "logos/dark/logo_primary.svg",
78
- "dark_logo": "logos/light/logo_primary.svg",
79
- "sidebar_hide_name": True,
80
- "footer_icons": [
81
- {
82
- "name": "GitHub",
83
- "url": "https://github.com/dylanljones/pyrekordbox",
84
- "html": """
85
- <svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
86
- <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
87
- </svg>
88
- """,
89
- "class": "",
90
- },
91
- ],
92
- }
93
-
94
- # html_title = f"{release} documentation"
95
-
96
- # -- Options for HTML output -------------------------------------------------
97
-
98
- # The theme to use for HTML and HTML Help pages. See the documentation for
99
- # a list of builtin themes.
100
- html_theme = "furo"
101
-
102
- # The name of the Pygments (syntax highlighting) style to use.
103
- # pygments_style = "sphinx"
104
- # pygments_dark_style = "monokai"
105
-
106
- # We need headers to be linkable to so ask MyST-Parser to autogenerate anchor IDs for
107
- # headers up to and including level 3.
108
- myst_heading_anchors = 3
109
-
110
- # Prettier support formatting some MyST syntax but not all, so let's disable the
111
- # unsupported yet still enabled by default ones.
112
- myst_disable_syntax = [
113
- "colon_fence",
114
- "myst_block_break",
115
- "myst_line_comment",
116
- "math_block",
117
- ]
118
-
119
-
120
- # Don't show type hints
121
- autodoc_typehints = "none"
122
-
123
- # Preserve order
124
- autodoc_member_order = "bysource"
125
-
126
-
127
- # -- Apidoc ------------------------------------------------------------------
128
-
129
- add_module_names = True
130
-
131
-
132
- # -- Autosummary -------------------------------------------------------------
133
-
134
- autosummary_generate = True
135
- # autosummary_imported_members = True
136
-
137
-
138
- # -- Numpy extension ---------------------------------------------------------
139
-
140
- numpydoc_use_plots = True
141
- # numpydoc_xref_param_type = True
142
- # numpydoc_xref_ignore = "all" # not working...
143
- numpydoc_show_class_members = False
144
-
145
-
146
- # -- Intersphinx -------------------------------------------------------------
147
-
148
- # taken from https://gist.github.com/bskinn/0e164963428d4b51017cebdb6cda5209
149
- intersphinx_mapping = {
150
- "python": (r"https://docs.python.org", None),
151
- "numpy": (r"https://docs.scipy.org/doc/numpy/", None),
152
- "np": (r"https://docs.scipy.org/doc/numpy/", None),
153
- "matplotlib": (r"https://matplotlib.org/", None),
154
- "<name>": ("https://docs.python.org/3/", None),
155
- }
156
-
157
-
158
- # -- Auto-run sphinx-apidoc --------------------------------------------------
159
-
160
-
161
- def run_apidoc(_):
162
- from sphinx.ext.apidoc import main
163
- import os
164
- import sys
165
-
166
- sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
167
- cur_dir = os.path.abspath(os.path.dirname(__file__))
168
- proj_dir = os.path.dirname(os.path.dirname(cur_dir))
169
- doc_dir = os.path.join(proj_dir, "docs")
170
- output_path = os.path.join(doc_dir, "source", "generated")
171
- module = os.path.join(proj_dir, "pyrekordbox")
172
- exclude = os.path.join(module, "tests")
173
- template_dir = os.path.join(doc_dir, "source", "_templates", "apidoc")
174
- main(["-fMeT", "-o", output_path, module, exclude, "--templatedir", template_dir])
175
-
176
-
177
- def setup(app):
178
- app.connect("builder-inited", run_apidoc)
@@ -1,22 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # Author: Dylan Jones
3
- # Date: 2022-04-10
4
-
5
- from .logger import logger
6
- from .config import show_config, get_config
7
- from .xml import RekordboxXml, XmlDuplicateError, XmlAttributeKeyError
8
- from .anlz import get_anlz_paths, walk_anlz_paths, read_anlz_files, AnlzFile
9
- from .mysettings import (
10
- get_mysetting_paths,
11
- read_mysetting_file,
12
- MySettingFile,
13
- MySetting2File,
14
- DjmMySettingFile,
15
- DevSettingFile,
16
- )
17
- from .db6 import Rekordbox6Database, open_rekordbox_database
18
-
19
- try:
20
- from ._version import version as __version__
21
- except ImportError: # pragma: no cover
22
- __version__ = "unknown"
@@ -1,204 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # Author: Dylan Jones
3
- # Date: 2023-08-15
4
-
5
- import os
6
- import re
7
- import sys
8
- import shutil
9
- import urllib.request
10
- from pathlib import Path
11
- from pyrekordbox.config import write_db6_key_cache, _cache_file
12
-
13
- KEY_SOURCES = [
14
- {
15
- "url": r"https://raw.githubusercontent.com/mganss/CueGen/19878e6eb3f586dee0eb3eb4f2ce3ef18309de9d/CueGen/Generator.cs", # noqa: E501
16
- "regex": re.compile(
17
- r'((.|\n)*)Config\.UseSqlCipher.*\?.*"(?P<dp>.*)".*:.*null',
18
- flags=re.IGNORECASE | re.MULTILINE,
19
- ),
20
- },
21
- {
22
- "url": r"https://raw.githubusercontent.com/dvcrn/go-rekordbox/8be6191ba198ed7abd4ad6406d177ed7b4f749b5/cmd/getencryptionkey/main.go", # noqa: E501
23
- "regex": re.compile(
24
- r'((.|\n)*)fmt\.Print\("(?P<dp>.*)"\)', flags=re.IGNORECASE | re.MULTILINE
25
- ),
26
- },
27
- ]
28
-
29
-
30
- class WorkingDir:
31
- def __init__(self, path):
32
- self._prev = Path.cwd()
33
- self.path = Path(path)
34
-
35
- def __enter__(self):
36
- if self.path != self._prev:
37
- self.path.mkdir(parents=True, exist_ok=True)
38
- os.chdir(self.path)
39
-
40
- def __exit__(self, exc_type, exc_val, exc_tb):
41
- if self.path != self._prev:
42
- os.chdir(self._prev)
43
-
44
-
45
- def clone_repo(https_url: str) -> Path:
46
- path = Path.cwd() / https_url.split("/")[-1]
47
- if not path.exists():
48
- os.system(f"git clone {https_url}")
49
- assert path.exists()
50
- else:
51
- print(f"Repository {https_url} already cloned")
52
- return path
53
-
54
-
55
- def clone_pysqlcipher3() -> Path:
56
- return clone_repo(r"https://github.com/coleifer/sqlcipher3")
57
-
58
-
59
- def clone_sqlcipher_amalgamation() -> Path:
60
- return clone_repo(r"https://github.com/geekbrother/sqlcipher-amalgamation")
61
-
62
-
63
- def patch_pysqlcipher_setup(pysqlcipher_dir, cryptolib="libcrypto.lib"):
64
- path = Path(pysqlcipher_dir, "setup.py")
65
-
66
- with open(path, "r") as fh:
67
- text = fh.read()
68
-
69
- if cryptolib:
70
- lib_old = "os.environ.get('OPENSSL_LIBNAME') or 'libeay32.lib'"
71
- lib_new = f"os.environ.get('OPENSSL_LIBNAME') or '{cryptolib}'"
72
- text = text.replace(lib_old, lib_new)
73
-
74
- with open(path, "w") as fh:
75
- fh.write(text)
76
-
77
-
78
- def prepare_pysqlcipher(pysqlcipher_dir: Path, amalgamation_src: Path):
79
- # Copy amalgamation files to pysqlcipher directory
80
- root = pysqlcipher_dir
81
- root.mkdir(parents=True, exist_ok=True)
82
- shutil.copy2(amalgamation_src / "sqlite3.c", root / "sqlite3.c")
83
- shutil.copy2(amalgamation_src / "sqlite3.h", root / "sqlite3.h")
84
-
85
-
86
- def install_pysqlcipher(
87
- tmpdir="pysqlcipher3",
88
- crypto_lib="libcrypto.lib",
89
- pyexecutable="",
90
- build=True,
91
- install=True,
92
- cleanup=True,
93
- ):
94
- if sys.platform != "win32":
95
- print("Not on Windows, aborting...")
96
- return
97
-
98
- tmpdir = Path(tmpdir)
99
- # Download pysqlcipher3 and prepare amalgamation build
100
- with WorkingDir(tmpdir):
101
- pysqlcipher_dir = clone_pysqlcipher3()
102
- amalgamation_dir = clone_sqlcipher_amalgamation()
103
- amalgamation_src = amalgamation_dir / "src"
104
-
105
- prepare_pysqlcipher(pysqlcipher_dir, amalgamation_src)
106
- if os.getenv("OPENSSL_LIBNAME") is None:
107
- print("No OPENSSL_LIBNAME environment variable found, updating `setup.py`!")
108
- patch_pysqlcipher_setup(pysqlcipher_dir, crypto_lib)
109
-
110
- # Build amalgamation and install pysqlcipher
111
- if not pyexecutable:
112
- pyexecutable = sys.executable
113
-
114
- with WorkingDir(pysqlcipher_dir):
115
- if build:
116
- # Build amalgamation
117
- print()
118
- os.system(f"{pyexecutable} setup.py build_static build")
119
- if install:
120
- # Install pysqlcipher package
121
- print()
122
- os.system(f"{pyexecutable} setup.py install")
123
-
124
- # Remove temporary files
125
- if cleanup:
126
- try:
127
- print()
128
- print("Cleaning up")
129
- tmpdir.unlink(missing_ok=True)
130
- except PermissionError as e:
131
- print()
132
- print(e)
133
- print(f"Could not remove temporary directory '{tmpdir}'!")
134
-
135
-
136
- def download_db6_key():
137
- dp = ""
138
- for source in KEY_SOURCES:
139
- url = source["url"]
140
- regex = source["regex"]
141
- print(f"Looking for key: {url}")
142
-
143
- res = urllib.request.urlopen(url)
144
- data = res.read().decode("utf-8")
145
- match = regex.match(data)
146
- if match:
147
- dp = match.group("dp")
148
- break
149
- if dp:
150
- print(f"Found key, updating cache file {_cache_file}")
151
- write_db6_key_cache(dp)
152
- else:
153
- print("No key found in the online sources.")
154
-
155
-
156
- def main():
157
- from argparse import ArgumentParser
158
-
159
- parser = ArgumentParser("pyrekordbox")
160
- subparsers = parser.add_subparsers(dest="command")
161
-
162
- # Download Rekordbx 6 database key command
163
- subparsers.add_parser(
164
- "download-key",
165
- help="Download the Rekordbox 6 database key from the internet "
166
- "and write it to the cache file.",
167
- )
168
-
169
- # Install pysqlcipher3 command (Windows only)
170
- install_parser = subparsers.add_parser(
171
- "install-sqlcipher",
172
- help="Build sqlcipher against amalgamation and install pysqlcipher3",
173
- )
174
- install_parser.add_argument(
175
- "-t",
176
- "--tmpdir",
177
- type=str,
178
- default=".tmp",
179
- help="Path for storing temporary data (default: '.tmp')",
180
- )
181
- install_parser.add_argument(
182
- "-l",
183
- "--cryptolib",
184
- type=str,
185
- default="libcrypto.lib",
186
- help="The name of the OpenSSl crypto libary (default: 'libcrypto.lib')",
187
- )
188
- install_parser.add_argument(
189
- "-b",
190
- "--buildonly",
191
- action="store_true",
192
- help="Don't install sqlcipher3, only build the amalgamation",
193
- )
194
-
195
- # Parse args and handle command
196
- args = parser.parse_args(sys.argv[1:])
197
- if args.command == "download-key":
198
- download_db6_key()
199
- elif args.command == "install-sqlcipher":
200
- install_pysqlcipher(args.tmpdir, args.cryptolib, install=not args.buildonly)
201
-
202
-
203
- if __name__ == "__main__":
204
- main()
@@ -1,16 +0,0 @@
1
- # file generated by setuptools_scm
2
- # don't change, don't track in version control
3
- TYPE_CHECKING = False
4
- if TYPE_CHECKING:
5
- from typing import Tuple, Union
6
- VERSION_TUPLE = Tuple[Union[int, str], ...]
7
- else:
8
- VERSION_TUPLE = object
9
-
10
- version: str
11
- __version__: str
12
- __version_tuple__: VERSION_TUPLE
13
- version_tuple: VERSION_TUPLE
14
-
15
- __version__ = version = '0.2.1'
16
- __version_tuple__ = version_tuple = (0, 2, 1)