pyrekordbox 0.2.2__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.
@@ -12,7 +12,6 @@ from sqlalchemy import create_engine, or_, event, MetaData
12
12
  from sqlalchemy.orm import Session
13
13
  from sqlalchemy.exc import NoResultFound
14
14
  from sqlalchemy.sql.sqltypes import DateTime, String
15
- import packaging.version
16
15
  from ..utils import get_rekordbox_pid
17
16
  from ..config import get_config
18
17
  from ..anlz import get_anlz_paths, read_anlz_files
@@ -23,24 +22,22 @@ from . import tables
23
22
 
24
23
  try:
25
24
  from sqlcipher3 import dbapi2 as sqlite3 # noqa
25
+
26
+ _sqlcipher_available = True
26
27
  except ImportError:
27
28
  import sqlite3
28
29
 
29
- MAX_VERSION = packaging.version.parse("6.6.5")
30
+ _sqlcipher_available = False
31
+
32
+ MAX_VERSION = "6.6.5"
30
33
 
31
34
  logger = logging.getLogger(__name__)
32
35
 
33
36
  rb6_config = get_config("rekordbox6")
34
37
 
35
38
 
36
- class IncompatibleVersionError(Exception):
37
- def __init__(self, rb_version):
38
- super().__init__(
39
- f"Incompatible rekordbox 6 version\n"
40
- f"Your are using rekordbox {rb_version} but the key extraction only works "
41
- f"for versions lower than {MAX_VERSION}.\n"
42
- "Please use the `key` parameter to manually provide the database key."
43
- )
39
+ class NoCachedKey(Exception):
40
+ pass
44
41
 
45
42
 
46
43
  def open_rekordbox_database(path=None, key="", unlock=True, sql_driver=None):
@@ -109,13 +106,16 @@ def open_rekordbox_database(path=None, key="", unlock=True, sql_driver=None):
109
106
 
110
107
  if unlock:
111
108
  if not key:
112
- ver = packaging.version.parse(rb6_config["version"])
113
- if ver >= MAX_VERSION:
114
- raise IncompatibleVersionError(rb6_config["version"])
115
109
  try:
116
110
  key = rb6_config["dp"]
117
111
  except KeyError:
118
- raise ValueError("Could not unlock database: No key found")
112
+ raise NoCachedKey(
113
+ "Could not unlock database: No key found\n"
114
+ f"If you are using Rekordbox>{MAX_VERSION} the key can not be "
115
+ f"extracted automatically!\n"
116
+ "Please use the CLI of pyrekordbox to download the key or "
117
+ "use the `key` parameter to manually provide the database key."
118
+ )
119
119
  logger.info("Key: %s", key)
120
120
  # Unlock database
121
121
  con.execute(f"PRAGMA key='{key}'")
@@ -207,14 +207,26 @@ class Rekordbox6Database:
207
207
  raise FileNotFoundError(f"File '{path}' does not exist!")
208
208
  # Open database
209
209
  if unlock:
210
- if "dp" in rb6_config:
211
- key = rb6_config["dp"]
210
+ if not _sqlcipher_available:
211
+ raise ImportError(
212
+ "Could not unlock database: 'sqlcipher3' package not found"
213
+ )
212
214
  if not key:
213
- ver = packaging.version.parse(rb6_config["version"])
214
- if ver >= MAX_VERSION:
215
- raise IncompatibleVersionError(rb6_config["version"])
216
- else:
217
- raise ValueError("Could not unlock database: No key found")
215
+ try:
216
+ key = rb6_config["dp"]
217
+ except KeyError:
218
+ raise NoCachedKey(
219
+ "Could not unlock database: No key found\n"
220
+ f"If you are using Rekordbox>{MAX_VERSION} the key cannot be "
221
+ f"extracted automatically!\n"
222
+ "Please use the CLI of pyrekordbox to download the key or "
223
+ "use the `key` parameter to manually provide it."
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
+
218
230
  logger.info("Key: %s", key)
219
231
  # Unlock database and create engine
220
232
  url = f"sqlite+pysqlcipher://:{key}@/{path}?"
@@ -1815,7 +1827,9 @@ class Rekordbox6Database:
1815
1827
  root = self.get_anlz_dir(content)
1816
1828
  return read_anlz_files(root)
1817
1829
 
1818
- 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
+ ):
1819
1833
  """Update the file path of a track in the Rekordbox v6 database.
1820
1834
 
1821
1835
  This changes the `FolderPath` entry in the ``DjmdContent`` table and the
@@ -1832,6 +1846,8 @@ class Rekordbox6Database:
1832
1846
  If True, the changes made are written to disc.
1833
1847
  check_path : bool, optional
1834
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.
1835
1851
 
1836
1852
  Examples
1837
1853
  --------
@@ -1848,7 +1864,7 @@ class Rekordbox6Database:
1848
1864
  Updating the path changes the database entry
1849
1865
 
1850
1866
  >>> new_path = "C:/Music/PioneerDJ/Sampler/PRESET ONESHOT/NOISE.wav"
1851
- >>> db.update_content_path(cont, new_path)
1867
+ >>> db.update_content_path(cont, path)
1852
1868
  >>> cont.FolderPath
1853
1869
  C:/Music/PioneerDJ/Sampler/PRESET ONESHOT/NOISE.wav
1854
1870
 
@@ -1882,15 +1898,31 @@ class Rekordbox6Database:
1882
1898
  logger.debug("Updating database file path: %s", path)
1883
1899
  content.FolderPath = path
1884
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
+
1885
1912
  if save:
1886
- logger.debug("Saving changes")
1913
+ logger.debug("Saving ANLZ files")
1887
1914
  # Save ANLZ files
1888
1915
  for anlz_path, anlz in anlz_files.items():
1889
1916
  anlz.save(anlz_path)
1917
+
1918
+ if commit:
1890
1919
  # Commit database changes
1920
+ logger.debug("Committing changes to the database")
1891
1921
  self.commit()
1892
1922
 
1893
- 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
+ ):
1894
1926
  """Update the file name of a track in the Rekordbox v6 database.
1895
1927
 
1896
1928
  This changes the `FolderPath` entry in the ``DjmdContent`` table and the
@@ -1907,6 +1939,8 @@ class Rekordbox6Database:
1907
1939
  If True, the changes made are written to disc.
1908
1940
  check_path : bool, optional
1909
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.
1910
1944
 
1911
1945
  See Also
1912
1946
  --------
@@ -1932,7 +1966,6 @@ class Rekordbox6Database:
1932
1966
  >>> file = list(files.values())[0]
1933
1967
  >>> cont.FolderPath == file.get("path")
1934
1968
  True
1935
-
1936
1969
  """
1937
1970
  if isinstance(content, (int, str)):
1938
1971
  content = self.get_content(ID=content)
@@ -1941,7 +1974,7 @@ class Rekordbox6Database:
1941
1974
  ext = old_path.suffix
1942
1975
  new_path = old_path.parent / name
1943
1976
  new_path = new_path.with_suffix(ext)
1944
- self.update_content_path(content, new_path, save, check_path)
1977
+ self.update_content_path(content, new_path, save, check_path, commit=commit)
1945
1978
 
1946
1979
  def to_dict(self, verbose=False):
1947
1980
  """Convert the database to a dictionary.
@@ -4,6 +4,7 @@
4
4
 
5
5
  import logging
6
6
  from contextlib import contextmanager
7
+ from sqlalchemy.orm.exc import ObjectDeletedError
7
8
 
8
9
  logger = logging.getLogger(__name__)
9
10
 
@@ -72,7 +73,12 @@ class RekordboxAgentRegistry:
72
73
  The table entry instance.
73
74
  """
74
75
  if cls.__enabled__:
75
- logger.debug("On delete: %s", instance)
76
+ try:
77
+ s = str(instance)
78
+ logger.debug("On delete: %s", s)
79
+ except ObjectDeletedError:
80
+ instance = []
81
+
76
82
  cls.__update_sequence__.append((instance, "delete", "", ""))
77
83
 
78
84
  @classmethod