pyrekordbox 0.2.3__py3-none-any.whl → 0.3.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.
docs/source/index.md CHANGED
@@ -11,7 +11,6 @@ sphinx-quickstart on Thu Apr 7 15:06:50 2022.
11
11
  [![Python][python-badge+]][pypi-link]
12
12
  [![Platform][platform-badge]][pypi-link]
13
13
  [![license: MIT][license-badge]][license-link]
14
- [![style: black][black-badge]][black-link]
15
14
 
16
15
  ```{admonition} Disclaimer
17
16
  This project is not affiliated with Pioneer Corp. or its related companies
@@ -45,6 +44,7 @@ caption: User Guide
45
44
 
46
45
  quickstart
47
46
  installation
47
+ key
48
48
  tutorial/index
49
49
  api
50
50
  ```
@@ -94,12 +94,10 @@ PyPI <https://pypi.org/project/pyrekordbox/>
94
94
  [platform-badge]: https://img.shields.io/badge/platform-win%20%7C%20osx-blue?style=flat
95
95
  [pypi-badge]: https://img.shields.io/pypi/v/pyrekordbox?style=flat
96
96
  [license-badge]: https://img.shields.io/pypi/l/pyrekordbox?color=lightgrey
97
- [black-badge]: https://img.shields.io/badge/code%20style-black-000000?style=flat
98
97
 
99
98
  [pypi-link]: https://pypi.org/project/pyrekordbox/
100
99
  [license-link]: https://github.com/dylanljones/pyrekordbox/blob/master/LICENSE
101
100
  [tests-link]: https://github.com/dylanljones/pyrekordbox/actions/workflows/tests.yml
102
- [black-link]: https://github.com/psf/black
103
101
  [codecov-link]: https://app.codecov.io/gh/dylanljones/pyrekordbox/tree/master
104
102
 
105
103
  [issue]: https://github.com/dylanljones/pyrekordbox/issues/64
@@ -20,7 +20,7 @@ $ pip install .
20
20
  ```
21
21
 
22
22
 
23
- ## Dependencies
23
+ ## Installing SQLCipher
24
24
 
25
25
  Unlocking the new Rekordbox 6 `master.db` database file requires [SQLCipher][sqlcipher].
26
26
  Pyrekordbox makes no attempt to download/install SQLCipher, as it is a
@@ -261,12 +261,6 @@ c.execute("PRAGMA key='password'")
261
261
  ````
262
262
 
263
263
 
264
- ## References:
265
-
266
- - [https://stackoverflow.com/questions/33618565/how-to-build-sql-cipher-python-binding-for-windows](https://stackoverflow.com/questions/33618565/how-to-build-sql-cipher-python-binding-for-windows)
267
- - [https://github.com/Monogi/pysqlcipher3_install_win10](https://github.com/Monogi/pysqlcipher3_install_win10)
268
-
269
-
270
264
 
271
265
  [VS]: https://visualstudio.microsoft.com/de/vs/community/
272
266
  [OpenSSL]: https://slproweb.com/products/Win32OpenSSL.html
docs/source/key.md ADDED
@@ -0,0 +1,104 @@
1
+ # Database key
2
+
3
+ If you are using Rekordbox v6.6.5 or later and have no cached key from a previous
4
+ Rekordbox version, the database can not be unlocked automatically.
5
+ However, the command line interface of ``pyrekordbox`` provides a command for downloading
6
+ the key from known sources and writing it to the cache file:
7
+ ````shell
8
+ python -m pyrekordbox download-key
9
+ ````
10
+ Once the key is cached the database can be opened without providing the key.
11
+ if you obtained the key from another source, you can also pass it to the database handler
12
+ ````python
13
+ db = Rekordbox6Database(key="<insert key here>")
14
+ ````
15
+ or write it to the cache file manually:
16
+ ````python
17
+ from pyrekordbox.config import write_db6_key_cache
18
+
19
+ write_db6_key_cache("<insert key here>") # call once
20
+ db = Rekordbox6Database()
21
+ ````
22
+
23
+ ## Alternative methods
24
+
25
+ The key can be extracted manually from the users machine. After the key is obtained
26
+ it can be writen to the cache file as described above.
27
+ The method varies depending on the operating system.
28
+
29
+ ### Windows
30
+
31
+ On Windows, the key can be extracted from the Rekordbox executable using a debugger:
32
+
33
+ 1. Download [x64dbg] and run it.
34
+
35
+ 2. Options -> Preferences. Make sure "Entry Breakpoint" is set in the Events tab.
36
+
37
+ 3. File -> Open... `rekordbox.exe` (the main application executable)
38
+
39
+ 4. Look at the status bar. It should have a yellow "Paused" icon followed by some status text.
40
+ Right now it should say "System breakpoint reached!"
41
+
42
+ 5. Hit F9 or press the Run button in the top bar. The status text should change to
43
+ "INT3 breakpoint 'entry breakpoint' at <rekordbox.EntryPoint>".
44
+
45
+ 6. Click in the disassembly window, then press Ctrl+G to open the Go To Expression box,
46
+ and search for `sqlite3_key_v2` and press OK. This should jump you to the code for
47
+ that function, which typically starts with `mov dword ptr ss:[rsp+xx], r9d` or similar.
48
+
49
+ 7. Without clicking anywhere on the disassembly window, press F2 to toggle breakpoint.
50
+ The top instruction's address should turn red.
51
+
52
+ 8. Hit F9 or press the Run button in the top bar. The status text will start changing
53
+ a bunch, while the program starts up. Wait until the status bar goes back to "Paused"
54
+ in yellow. If the status text says something like "First chance exception on..."
55
+ press F9 again.
56
+
57
+ 9. The status bar should go to "Paused" in yellow again, this time with status text
58
+ that says "INT3 breakpoint at <sqlite3.sqlite3_key_v2>". This means our breakpoint
59
+ has been hit.
60
+
61
+ 10. Click the register panel (top right, where RAX, RBX, RCX, etc. are listed) so
62
+ it updates. Right click the red address next to R8, and click "Follow in dump".
63
+
64
+ 11. The dump at the bottom left will move to that address. Right click the dump panel
65
+ and select Text -> ASCII at the bottom. You should now see the key as a string.
66
+ You can drag-select it, then right click to copy selected line.
67
+
68
+ 12. Go to Debug -> Close to close the process, then close x64dbg.
69
+
70
+
71
+ ```{figure} /_static/images/x64dbg_rb_key.png
72
+ :align: center
73
+ :scale: 60
74
+ ```
75
+
76
+
77
+ ### MacOS
78
+
79
+ On MacOS, the key can be extracted using the [RekordLocksmith] tool:
80
+
81
+ 1. Install LLDB:
82
+ - LLDB can be installed with Xcode on macOS via the App Store or xcode-cli-commands.
83
+ - Ensure LLDB is accessible from the terminal by running `lldb` in the terminal.
84
+
85
+ 2. Disable SIP:
86
+ - Restart your Mac and hold down `Command-R` as it boots to enter Recovery Mode.
87
+ - Open the Terminal from the Utilities menu.
88
+ - Type `csrutil disable` and press Enter.
89
+ - Restart your Mac.
90
+
91
+ 3. Download RekordLocksmith:
92
+ - Clone or download the [RekordLocksmith] repository from GitHub.
93
+
94
+ 4. Run RekordLocksmith:
95
+ - Use the terminal to navigate to the folder containing `rekordlocksmith.py`.
96
+ - Run the script:
97
+ ````shell
98
+ python3 rekordlocksmith.py /Applications/rekordbox\ 6/rekordbox.app/Contents/MacOS/rekordbox
99
+ ````
100
+ The tool will output the database key to the terminal and save it to a file named `rekordbox_db_pass.txt` in the current directory.
101
+
102
+
103
+ [x64dbg]: https://x64dbg.com/
104
+ [RekordLocksmith]: https://github.com/Bide-UK/rekordlocksmith#rekordlocksmith
docs/source/quickstart.md CHANGED
@@ -101,7 +101,7 @@ between two databases.
101
101
  Pyrekordbox can read and write Rekordbox XML databases.
102
102
 
103
103
  ````python
104
- from pyrekordbox.xml import RekordboxXml
104
+ from pyrekordbox.rbxml import RekordboxXml
105
105
 
106
106
  xml = RekordboxXml("database.xml")
107
107
 
pyrekordbox/__init__.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  from .logger import logger
6
6
  from .config import show_config, get_config, update_config
7
- from .xml import RekordboxXml, XmlDuplicateError, XmlAttributeKeyError
7
+ from .rbxml import RekordboxXml, XmlDuplicateError, XmlAttributeKeyError
8
8
  from .anlz import get_anlz_paths, walk_anlz_paths, read_anlz_files, AnlzFile
9
9
  from .mysettings import (
10
10
  get_mysetting_paths,
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.2.3'
16
- __version_tuple__ = version_tuple = (0, 2, 3)
15
+ __version__ = version = '0.3.0'
16
+ __version_tuple__ = version_tuple = (0, 3, 0)
pyrekordbox/anlz/file.py CHANGED
@@ -114,10 +114,10 @@ class AnlzFile(abc.Mapping):
114
114
  # Check if the file is garbled (only on exported files)
115
115
  # For this we check the validity of mood and bank
116
116
  # Mood: High=1, Mid=2, Low=3
117
- # Bank: 1-8
117
+ # Bank: 0-8
118
118
  mood = Int16ub.parse(tag_data[18:20])
119
119
  bank = Int16ub.parse(tag_data[28:30])
120
- if 1 <= mood <= 3 and 1 <= bank <= 8:
120
+ if 1 <= mood <= 3 and 0 <= bank <= 8:
121
121
  logger.debug("PSSI is not garbled!")
122
122
  else:
123
123
  logger.debug("PSSI is garbled!")
@@ -222,6 +222,11 @@ class Rekordbox6Database:
222
222
  "Please use the CLI of pyrekordbox to download the key or "
223
223
  "use the `key` parameter to manually provide it."
224
224
  )
225
+ else:
226
+ # Check if key looks like a valid key
227
+ if not key.startswith("402fd"):
228
+ raise ValueError("The provided database key doesn't look valid!")
229
+
225
230
  logger.info("Key: %s", key)
226
231
  # Unlock database and create engine
227
232
  url = f"sqlite+pysqlcipher://:{key}@/{path}?"
@@ -1822,7 +1827,9 @@ class Rekordbox6Database:
1822
1827
  root = self.get_anlz_dir(content)
1823
1828
  return read_anlz_files(root)
1824
1829
 
1825
- def update_content_path(self, content, path, save=True, check_path=True):
1830
+ def update_content_path(
1831
+ self, content, path, save=True, check_path=True, commit=True
1832
+ ):
1826
1833
  """Update the file path of a track in the Rekordbox v6 database.
1827
1834
 
1828
1835
  This changes the `FolderPath` entry in the ``DjmdContent`` table and the
@@ -1839,6 +1846,8 @@ class Rekordbox6Database:
1839
1846
  If True, the changes made are written to disc.
1840
1847
  check_path : bool, optional
1841
1848
  If True, raise an assertion error if the given file path does not exist.
1849
+ commit : bool, optional
1850
+ If True, the changes are committed to the database. True by default.
1842
1851
 
1843
1852
  Examples
1844
1853
  --------
@@ -1855,7 +1864,7 @@ class Rekordbox6Database:
1855
1864
  Updating the path changes the database entry
1856
1865
 
1857
1866
  >>> new_path = "C:/Music/PioneerDJ/Sampler/PRESET ONESHOT/NOISE.wav"
1858
- >>> db.update_content_path(cont, new_path)
1867
+ >>> db.update_content_path(cont, path)
1859
1868
  >>> cont.FolderPath
1860
1869
  C:/Music/PioneerDJ/Sampler/PRESET ONESHOT/NOISE.wav
1861
1870
 
@@ -1889,15 +1898,31 @@ class Rekordbox6Database:
1889
1898
  logger.debug("Updating database file path: %s", path)
1890
1899
  content.FolderPath = path
1891
1900
 
1901
+ # Update the OrgFolderPath column with the new path
1902
+ # if the column matches the old_path variable
1903
+ org_folder_path = content.OrgFolderPath
1904
+ if org_folder_path == old_path:
1905
+ content.OrgFolderPath = path
1906
+
1907
+ # Update the FileNameL column with the new filename if it changed
1908
+ new_name = path.split("/")[-1]
1909
+ if content.FileNameL != new_name:
1910
+ content.FileNameL = new_name
1911
+
1892
1912
  if save:
1893
- logger.debug("Saving changes")
1913
+ logger.debug("Saving ANLZ files")
1894
1914
  # Save ANLZ files
1895
1915
  for anlz_path, anlz in anlz_files.items():
1896
1916
  anlz.save(anlz_path)
1917
+
1918
+ if commit:
1897
1919
  # Commit database changes
1920
+ logger.debug("Committing changes to the database")
1898
1921
  self.commit()
1899
1922
 
1900
- def update_content_filename(self, content, name, save=True, check_path=True):
1923
+ def update_content_filename(
1924
+ self, content, name, save=True, check_path=True, commit=True
1925
+ ):
1901
1926
  """Update the file name of a track in the Rekordbox v6 database.
1902
1927
 
1903
1928
  This changes the `FolderPath` entry in the ``DjmdContent`` table and the
@@ -1914,6 +1939,8 @@ class Rekordbox6Database:
1914
1939
  If True, the changes made are written to disc.
1915
1940
  check_path : bool, optional
1916
1941
  If True, raise an assertion error if the new file path does not exist.
1942
+ commit : bool, optional
1943
+ If True, the changes are committed to the database. True by default.
1917
1944
 
1918
1945
  See Also
1919
1946
  --------
@@ -1939,7 +1966,6 @@ class Rekordbox6Database:
1939
1966
  >>> file = list(files.values())[0]
1940
1967
  >>> cont.FolderPath == file.get("path")
1941
1968
  True
1942
-
1943
1969
  """
1944
1970
  if isinstance(content, (int, str)):
1945
1971
  content = self.get_content(ID=content)
@@ -1948,7 +1974,7 @@ class Rekordbox6Database:
1948
1974
  ext = old_path.suffix
1949
1975
  new_path = old_path.parent / name
1950
1976
  new_path = new_path.with_suffix(ext)
1951
- self.update_content_path(content, new_path, save, check_path)
1977
+ self.update_content_path(content, new_path, save, check_path, commit=commit)
1952
1978
 
1953
1979
  def to_dict(self, verbose=False):
1954
1980
  """Convert the database to a dictionary.