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.
- docs/source/formats/anlz.md +178 -7
- docs/source/formats/db6.md +1 -1
- docs/source/index.md +2 -6
- docs/source/quickstart.md +68 -45
- docs/source/tutorial/index.md +1 -1
- pyrekordbox/__init__.py +1 -1
- pyrekordbox/_version.py +2 -2
- pyrekordbox/anlz/file.py +39 -0
- pyrekordbox/anlz/structs.py +3 -5
- pyrekordbox/config.py +71 -27
- pyrekordbox/db6/database.py +260 -33
- pyrekordbox/db6/registry.py +22 -0
- pyrekordbox/db6/tables.py +3 -4
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/METADATA +12 -11
- pyrekordbox-0.2.2.dist-info/RECORD +80 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/top_level.txt +0 -2
- tests/test_config.py +175 -0
- tests/test_db6.py +78 -0
- build/lib/build/lib/docs/source/conf.py +0 -178
- build/lib/build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/build/lib/pyrekordbox/_version.py +0 -16
- build/lib/build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/build/lib/pyrekordbox/config.py +0 -596
- build/lib/build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/build/lib/pyrekordbox/logger.py +0 -23
- build/lib/build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/build/lib/pyrekordbox/utils.py +0 -162
- build/lib/build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/build/lib/tests/__init__.py +0 -3
- build/lib/build/lib/tests/test_anlz.py +0 -206
- build/lib/build/lib/tests/test_db6.py +0 -1039
- build/lib/build/lib/tests/test_mysetting.py +0 -203
- build/lib/build/lib/tests/test_xml.py +0 -629
- build/lib/docs/source/conf.py +0 -178
- build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/pyrekordbox/_version.py +0 -16
- build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/pyrekordbox/config.py +0 -596
- build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/pyrekordbox/logger.py +0 -23
- build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/pyrekordbox/utils.py +0 -162
- build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/tests/__init__.py +0 -3
- build/lib/tests/test_anlz.py +0 -206
- build/lib/tests/test_db6.py +0 -1039
- build/lib/tests/test_mysetting.py +0 -203
- build/lib/tests/test_xml.py +0 -629
- pyrekordbox-0.2.1.dist-info/RECORD +0 -129
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/LICENSE +0 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/WHEEL +0 -0
pyrekordbox/db6/database.py
CHANGED
@@ -12,7 +12,7 @@ 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
|
-
|
15
|
+
import packaging.version
|
16
16
|
from ..utils import get_rekordbox_pid
|
17
17
|
from ..config import get_config
|
18
18
|
from ..anlz import get_anlz_paths, read_anlz_files
|
@@ -26,7 +26,7 @@ try:
|
|
26
26
|
except ImportError:
|
27
27
|
import sqlite3
|
28
28
|
|
29
|
-
MAX_VERSION = version.parse("6.6.5")
|
29
|
+
MAX_VERSION = packaging.version.parse("6.6.5")
|
30
30
|
|
31
31
|
logger = logging.getLogger(__name__)
|
32
32
|
|
@@ -109,7 +109,7 @@ def open_rekordbox_database(path=None, key="", unlock=True, sql_driver=None):
|
|
109
109
|
|
110
110
|
if unlock:
|
111
111
|
if not key:
|
112
|
-
ver = version.parse(rb6_config["version"])
|
112
|
+
ver = packaging.version.parse(rb6_config["version"])
|
113
113
|
if ver >= MAX_VERSION:
|
114
114
|
raise IncompatibleVersionError(rb6_config["version"])
|
115
115
|
try:
|
@@ -207,15 +207,15 @@ 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
212
|
if not key:
|
211
|
-
ver = version.parse(rb6_config["version"])
|
213
|
+
ver = packaging.version.parse(rb6_config["version"])
|
212
214
|
if ver >= MAX_VERSION:
|
213
215
|
raise IncompatibleVersionError(rb6_config["version"])
|
214
|
-
|
215
|
-
key = rb6_config["dp"]
|
216
|
-
except KeyError:
|
216
|
+
else:
|
217
217
|
raise ValueError("Could not unlock database: No key found")
|
218
|
-
|
218
|
+
logger.info("Key: %s", key)
|
219
219
|
# Unlock database and create engine
|
220
220
|
url = f"sqlite+pysqlcipher://:{key}@/{path}?"
|
221
221
|
engine = create_engine(url, module=sqlite3)
|
@@ -925,12 +925,12 @@ class Rekordbox6Database:
|
|
925
925
|
.order_by(tables.DjmdSongPlaylist.TrackNo)
|
926
926
|
)
|
927
927
|
moved = list()
|
928
|
-
self.registry.
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
928
|
+
with self.registry.disabled():
|
929
|
+
for song in query:
|
930
|
+
song.TrackNo -= 1
|
931
|
+
song.updated_at = now
|
932
|
+
moved.append(song)
|
933
|
+
|
934
934
|
if moved:
|
935
935
|
self.registry.on_move(moved)
|
936
936
|
|
@@ -1092,9 +1092,8 @@ class Rekordbox6Database:
|
|
1092
1092
|
)
|
1093
1093
|
for pl in query:
|
1094
1094
|
pl.Seq += 1
|
1095
|
-
self.registry.
|
1096
|
-
|
1097
|
-
self.registry.enable_tracking()
|
1095
|
+
with self.registry.disabled():
|
1096
|
+
pl.updated_at = now
|
1098
1097
|
|
1099
1098
|
# Add new playlist to database
|
1100
1099
|
# First create with name 'New playlist'
|
@@ -1390,10 +1389,9 @@ class Rekordbox6Database:
|
|
1390
1389
|
# Set seq number and update time *before* other playlists to ensure
|
1391
1390
|
# right USN increment order
|
1392
1391
|
playlist.ParentID = parent_id
|
1393
|
-
self.registry.
|
1394
|
-
|
1395
|
-
|
1396
|
-
self.registry.enable_tracking()
|
1392
|
+
with self.registry.disabled():
|
1393
|
+
playlist.Seq = seq
|
1394
|
+
playlist.updated_at = now
|
1397
1395
|
|
1398
1396
|
if not insert_at_end:
|
1399
1397
|
# Update seq numbers higher than the new seq number in *new* parent
|
@@ -1402,9 +1400,8 @@ class Rekordbox6Database:
|
|
1402
1400
|
# Update time of other playlists are left unchanged
|
1403
1401
|
pl.Seq += 1
|
1404
1402
|
# Each move counts as one USN increment, so disable for update time
|
1405
|
-
self.registry.
|
1406
|
-
|
1407
|
-
self.registry.enable_tracking()
|
1403
|
+
with self.registry.disabled():
|
1404
|
+
pl.updated_at = now
|
1408
1405
|
|
1409
1406
|
# Update seq numbers higher than the old seq number in *old* parent
|
1410
1407
|
# USN is not updated here
|
@@ -1463,17 +1460,15 @@ class Rekordbox6Database:
|
|
1463
1460
|
# right USN increment order
|
1464
1461
|
playlist.Seq = seq
|
1465
1462
|
# Each move counts as one USN increment, so disable for update time
|
1466
|
-
self.registry.
|
1467
|
-
|
1468
|
-
self.registry.enable_tracking()
|
1463
|
+
with self.registry.disabled():
|
1464
|
+
playlist.updated_at = now
|
1469
1465
|
|
1470
1466
|
# Set seq number and update time for playlists between old_seq and seq
|
1471
1467
|
for pl in other_playlists:
|
1472
1468
|
pl.Seq += delta_seq
|
1473
1469
|
# Each move counts as one USN increment, so disable for update time
|
1474
|
-
self.registry.
|
1475
|
-
|
1476
|
-
self.registry.enable_tracking()
|
1470
|
+
with self.registry.disabled():
|
1471
|
+
pl.updated_at = now
|
1477
1472
|
|
1478
1473
|
def rename_playlist(self, playlist, name):
|
1479
1474
|
"""Renames a playlist or playlist folder.
|
@@ -1509,9 +1504,241 @@ class Rekordbox6Database:
|
|
1509
1504
|
# Update name of playlist
|
1510
1505
|
playlist.Name = name
|
1511
1506
|
# Update update time: USN not incremented
|
1512
|
-
self.registry.
|
1513
|
-
|
1514
|
-
|
1507
|
+
with self.registry.disabled():
|
1508
|
+
playlist.updated_at = now
|
1509
|
+
|
1510
|
+
def add_album(
|
1511
|
+
self, name, artist=None, image_path=None, compilation=None, search_str=None
|
1512
|
+
):
|
1513
|
+
"""Adds a new album to the database.
|
1514
|
+
|
1515
|
+
Parameters
|
1516
|
+
----------
|
1517
|
+
name : str
|
1518
|
+
The name of the album. Must be a unique name (case-sensitive).
|
1519
|
+
If an album with the same name already exists in the database,
|
1520
|
+
use the `ID` of the existing album instead.
|
1521
|
+
artist : str or int or DjmdArtist, optional
|
1522
|
+
The artist of the album. Can either be a :class:`DjmdArtist` object
|
1523
|
+
or an artist ID.
|
1524
|
+
image_path : str, optional
|
1525
|
+
The path to the album cover image.
|
1526
|
+
compilation : bool, optional
|
1527
|
+
Whether the album is a compilation album. If not given, the
|
1528
|
+
default value of `False` is used.
|
1529
|
+
search_str : str, optional
|
1530
|
+
The search string of the album.
|
1531
|
+
|
1532
|
+
Returns
|
1533
|
+
-------
|
1534
|
+
album : DjmdAlbum
|
1535
|
+
The newly created album.
|
1536
|
+
|
1537
|
+
Raises
|
1538
|
+
------
|
1539
|
+
ValueError : If an album with the same name already exists in the database.
|
1540
|
+
|
1541
|
+
Examples
|
1542
|
+
--------
|
1543
|
+
Add a new album to the database:
|
1544
|
+
|
1545
|
+
>>> db = Rekordbox6Database()
|
1546
|
+
>>> db.add_album(name="Album 1")
|
1547
|
+
<DjmdAlbum(148754249 Name=Album 1)>
|
1548
|
+
|
1549
|
+
Add a new album to the database with an album artist:
|
1550
|
+
|
1551
|
+
>>> artist = db.get_artist(Name="Artist 1").one() # noqa
|
1552
|
+
>>> db.add_album(name="Album 2", artist=artist)
|
1553
|
+
<DjmdAlbum(148754249 Name=Album 2)>
|
1554
|
+
|
1555
|
+
For setting the album of a track, the usual procedure is to first
|
1556
|
+
check if an entry with the same album name already exists in the database,
|
1557
|
+
and if not, add a new album:
|
1558
|
+
|
1559
|
+
>>> name = "Album name"
|
1560
|
+
>>> content = db.get_content().one()
|
1561
|
+
>>> album = db.get_album(Name=name).one_or_none()
|
1562
|
+
>>> if album is None:
|
1563
|
+
... album = db.add_album(name=name)
|
1564
|
+
>>> content.AlbumID = album.ID
|
1565
|
+
"""
|
1566
|
+
# Check if album already exists
|
1567
|
+
query = self.query(tables.DjmdAlbum).filter_by(Name=name)
|
1568
|
+
if query.count() > 0:
|
1569
|
+
raise ValueError(f"Album '{name}' already exists in database")
|
1570
|
+
|
1571
|
+
# Get artist ID
|
1572
|
+
if artist is not None:
|
1573
|
+
if isinstance(artist, (int, str)):
|
1574
|
+
artist = self.get_artist(ID=artist)
|
1575
|
+
artist = artist.ID
|
1576
|
+
|
1577
|
+
album = tables.DjmdAlbum.create(
|
1578
|
+
ID=self.generate_unused_id(tables.DjmdAlbum),
|
1579
|
+
Name=name,
|
1580
|
+
AlbumArtistID=artist,
|
1581
|
+
ImagePath=image_path,
|
1582
|
+
Compilation=compilation,
|
1583
|
+
SearchStr=search_str,
|
1584
|
+
)
|
1585
|
+
self.add(album)
|
1586
|
+
self.flush()
|
1587
|
+
return album
|
1588
|
+
|
1589
|
+
def add_artist(self, name, search_str=None):
|
1590
|
+
"""Adds a new artist to the database.
|
1591
|
+
|
1592
|
+
Parameters
|
1593
|
+
----------
|
1594
|
+
name : str
|
1595
|
+
The name of the artist. Must be a unique name (case-sensitive).
|
1596
|
+
If an artist with the same name already exists in the database,
|
1597
|
+
use the `ID` of the existing artist instead.
|
1598
|
+
search_str : str, optional
|
1599
|
+
The search string of the artist.
|
1600
|
+
|
1601
|
+
Returns
|
1602
|
+
-------
|
1603
|
+
artist : DjmdArtist
|
1604
|
+
The newly created artist.
|
1605
|
+
|
1606
|
+
Raises
|
1607
|
+
------
|
1608
|
+
ValueError : If an artist with the same name already exists in the database.
|
1609
|
+
|
1610
|
+
Examples
|
1611
|
+
--------
|
1612
|
+
Add a new artist to the database:
|
1613
|
+
|
1614
|
+
>>> db = Rekordbox6Database()
|
1615
|
+
>>> db.add_artist(name="Artist 1")
|
1616
|
+
<DjmdArtist(123456789, Name='Artist 1')>
|
1617
|
+
|
1618
|
+
Add a new artist to the database with a custom search string:
|
1619
|
+
|
1620
|
+
>>> db.add_artist(name="Artist 2", search_str="artist 2")
|
1621
|
+
<DjmdArtist(123456789, Name='Artist 2')>
|
1622
|
+
|
1623
|
+
For setting the artist of a track, the usual procedure is to first
|
1624
|
+
check if an entry with the same artist name already exists in the database,
|
1625
|
+
and if not, add a new artist:
|
1626
|
+
|
1627
|
+
>>> name = "Artist name"
|
1628
|
+
>>> content = db.get_content().one()
|
1629
|
+
>>> artist = db.get_artist(Name=name).one_or_none()
|
1630
|
+
>>> if artist is None:
|
1631
|
+
... artist = db.add_artist(name=name)
|
1632
|
+
>>> content.ArtistID = artist.ID
|
1633
|
+
"""
|
1634
|
+
# Check if artist already exists
|
1635
|
+
query = self.query(tables.DjmdArtist).filter_by(Name=name)
|
1636
|
+
if query.count() > 0:
|
1637
|
+
raise ValueError(f"Artist '{name}' already exists in database")
|
1638
|
+
|
1639
|
+
id_ = self.generate_unused_id(tables.DjmdArtist)
|
1640
|
+
artist = tables.DjmdArtist.create(ID=id_, Name=name, SearchStr=search_str)
|
1641
|
+
self.add(artist)
|
1642
|
+
self.flush()
|
1643
|
+
return artist
|
1644
|
+
|
1645
|
+
def add_genre(self, name):
|
1646
|
+
"""Adds a new genre to the database.
|
1647
|
+
|
1648
|
+
Parameters
|
1649
|
+
----------
|
1650
|
+
name : str
|
1651
|
+
The name of the genre. Must be a unique name (case-sensitive).
|
1652
|
+
If a genre with the same name already exists in the database,
|
1653
|
+
use the `ID` of the existing genre instead.
|
1654
|
+
|
1655
|
+
Returns
|
1656
|
+
-------
|
1657
|
+
genre : DjmdGenre
|
1658
|
+
The newly created genre.
|
1659
|
+
|
1660
|
+
Raises
|
1661
|
+
------
|
1662
|
+
ValueError : If a genre with the same name already exists in the database.
|
1663
|
+
|
1664
|
+
Examples
|
1665
|
+
--------
|
1666
|
+
Add a new genre to the database:
|
1667
|
+
|
1668
|
+
>>> db = Rekordbox6Database()
|
1669
|
+
>>> db.add_genre(name="Genre 1")
|
1670
|
+
<DjmdGenre(123456789 Name=Genre 1)>
|
1671
|
+
|
1672
|
+
For setting the genre of a track, the usual procedure is to first
|
1673
|
+
check if an entry with the same genre name already exists in the database,
|
1674
|
+
and if not, add a new genre:
|
1675
|
+
|
1676
|
+
>>> name = "Genre name"
|
1677
|
+
>>> content = db.get_content().one()
|
1678
|
+
>>> genre = db.get_genre(Name=name).one_or_none()
|
1679
|
+
>>> if genre is None:
|
1680
|
+
... genre = db.add_genre(name=name)
|
1681
|
+
>>> content.GenreID = genre.ID
|
1682
|
+
"""
|
1683
|
+
# Check if genre already exists
|
1684
|
+
query = self.query(tables.DjmdGenre).filter_by(Name=name)
|
1685
|
+
if query.count() > 0:
|
1686
|
+
raise ValueError(f"Genre '{name}' already exists in database")
|
1687
|
+
|
1688
|
+
id_ = self.generate_unused_id(tables.DjmdGenre)
|
1689
|
+
genre = tables.DjmdGenre.create(ID=id_, Name=name)
|
1690
|
+
self.add(genre)
|
1691
|
+
self.flush()
|
1692
|
+
return genre
|
1693
|
+
|
1694
|
+
def add_label(self, name):
|
1695
|
+
"""Adds a new label to the database.
|
1696
|
+
|
1697
|
+
Parameters
|
1698
|
+
----------
|
1699
|
+
name : str
|
1700
|
+
The name of the label. Must be a unique name (case-sensitive).
|
1701
|
+
If a label with the same name already exists in the database,
|
1702
|
+
use the `ID` of the existing label instead.
|
1703
|
+
|
1704
|
+
Returns
|
1705
|
+
-------
|
1706
|
+
label : DjmdLabel
|
1707
|
+
The newly created label.
|
1708
|
+
|
1709
|
+
Raises
|
1710
|
+
------
|
1711
|
+
ValueError : If a label with the same name already exists in the database.
|
1712
|
+
|
1713
|
+
Examples
|
1714
|
+
--------
|
1715
|
+
Add a new label to the database:
|
1716
|
+
|
1717
|
+
>>> db = Rekordbox6Database()
|
1718
|
+
>>> db.add_label(name="Label 1")
|
1719
|
+
<DjmdLabel(123456789 Name=Label 1)>
|
1720
|
+
|
1721
|
+
For setting the label of a track, the usual procedure is to first
|
1722
|
+
check if an entry with the same label name already exists in the database,
|
1723
|
+
and if not, add a new label:
|
1724
|
+
|
1725
|
+
>>> name = "Label name"
|
1726
|
+
>>> content = db.get_content().one()
|
1727
|
+
>>> label = db.get_label(Name=name).one_or_none()
|
1728
|
+
>>> if label is None:
|
1729
|
+
... label = db.add_label(name=name)
|
1730
|
+
>>> content.LabelID = label.ID
|
1731
|
+
"""
|
1732
|
+
# Check if label already exists
|
1733
|
+
query = self.query(tables.DjmdLabel).filter_by(Name=name)
|
1734
|
+
if query.count() > 0:
|
1735
|
+
raise ValueError(f"Label '{name}' already exists in database")
|
1736
|
+
|
1737
|
+
id_ = self.generate_unused_id(tables.DjmdLabel)
|
1738
|
+
label = tables.DjmdLabel.create(ID=id_, Name=name)
|
1739
|
+
self.add(label)
|
1740
|
+
self.flush()
|
1741
|
+
return label
|
1515
1742
|
|
1516
1743
|
# ----------------------------------------------------------------------------------
|
1517
1744
|
|
pyrekordbox/db6/registry.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# Date: 2023-08-07
|
4
4
|
|
5
5
|
import logging
|
6
|
+
from contextlib import contextmanager
|
6
7
|
|
7
8
|
logger = logging.getLogger(__name__)
|
8
9
|
|
@@ -103,6 +104,27 @@ class RekordboxAgentRegistry:
|
|
103
104
|
"""Disables the tracking of database changes."""
|
104
105
|
cls.__enabled__ = False
|
105
106
|
|
107
|
+
@classmethod
|
108
|
+
@contextmanager
|
109
|
+
def disabled(cls):
|
110
|
+
"""Context manager to temporarily disable the tracking of database changes.
|
111
|
+
|
112
|
+
Examples
|
113
|
+
--------
|
114
|
+
>>> registry = RekordboxAgentRegistry(db) # noqa
|
115
|
+
>>> registry.__enabled__
|
116
|
+
True
|
117
|
+
|
118
|
+
>>> with registry.disabled():
|
119
|
+
... print(registry.__enabled__)
|
120
|
+
False
|
121
|
+
"""
|
122
|
+
enabled = cls.__enabled__
|
123
|
+
cls.disable_tracking()
|
124
|
+
yield cls
|
125
|
+
if enabled:
|
126
|
+
cls.enable_tracking()
|
127
|
+
|
106
128
|
def get_registries(self):
|
107
129
|
"""Returns all agent registries.
|
108
130
|
|
pyrekordbox/db6/tables.py
CHANGED
@@ -98,10 +98,9 @@ class Base(DeclarativeBase):
|
|
98
98
|
|
99
99
|
@classmethod
|
100
100
|
def create(cls, **kwargs):
|
101
|
-
RekordboxAgentRegistry.
|
102
|
-
|
103
|
-
|
104
|
-
RekordboxAgentRegistry.enable_tracking()
|
101
|
+
with RekordboxAgentRegistry.disabled():
|
102
|
+
# noinspection PyArgumentList
|
103
|
+
self = cls(**kwargs)
|
105
104
|
return self
|
106
105
|
|
107
106
|
@classmethod
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pyrekordbox
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
Summary: Inofficial Python package for interacting with the library of Pioneers Rekordbox DJ software.
|
5
5
|
Author-email: Dylan Jones <dylanljones94@gmail.com>
|
6
6
|
License: MIT License
|
@@ -50,19 +50,14 @@ License-File: LICENSE
|
|
50
50
|
Requires-Dist: bidict >=0.21.0
|
51
51
|
Requires-Dist: blowfish >=0.6.0
|
52
52
|
Requires-Dist: construct >=2.10.0
|
53
|
-
Requires-Dist: hypothesis >=6.0.0
|
54
53
|
Requires-Dist: numpy >=1.19.0
|
55
54
|
Requires-Dist: packaging
|
56
|
-
Requires-Dist: pytest >=6.2.0
|
57
55
|
Requires-Dist: psutil >=5.9.0
|
58
|
-
Requires-Dist: setuptools >=60.0.0
|
59
|
-
Requires-Dist: setuptools-scm[toml] >=4
|
60
56
|
Requires-Dist: sqlalchemy >=2.0.0
|
61
|
-
Provides-Extra: build
|
62
|
-
Requires-Dist: wheel >=0.37.0 ; extra == 'build'
|
63
57
|
Provides-Extra: test
|
58
|
+
Requires-Dist: hypothesis >=6.0.0 ; extra == 'test'
|
59
|
+
Requires-Dist: pytest >=6.2.0 ; extra == 'test'
|
64
60
|
Requires-Dist: pytest-cov ; extra == 'test'
|
65
|
-
Requires-Dist: wheel >=0.37.0 ; extra == 'test'
|
66
61
|
|
67
62
|
|
68
63
|
<p align="center">
|
@@ -88,7 +83,7 @@ Pioneers Rekordbox DJ Software. It currently supports
|
|
88
83
|
- Analysis files (ANLZ)
|
89
84
|
- My-Setting files
|
90
85
|
|
91
|
-
Tested Rekordbox versions: ``5.8.6 | 6.5.3``
|
86
|
+
Tested Rekordbox versions: ``5.8.6 | 6.5.3 | 6.7.7``
|
92
87
|
|
93
88
|
|
94
89
|
|⚠️| This project is still under development and might contain bugs or have breaking API changes in the future. |
|
@@ -112,7 +107,7 @@ where `VERSION` is a release, tag or branch name.
|
|
112
107
|
|
113
108
|
Unlocking the new Rekordbox 6 `master.db` database file requires [SQLCipher][sqlcipher].
|
114
109
|
Pyrekordbox makes no attempt to download/install SQLCipher, as it is a
|
115
|
-
pure Python package - whereas the SQLCipher/
|
110
|
+
pure Python package - whereas the SQLCipher/sqlcipher3 installation is
|
116
111
|
platform-dependent and can not be installed via ``pip``.
|
117
112
|
|
118
113
|
#### Windows
|
@@ -205,7 +200,7 @@ So far only a few tables support adding or deleting entries:
|
|
205
200
|
- ``DjmdSongPlaylist``: Songs in a playlist
|
206
201
|
|
207
202
|
Starting from Rekordbox version ``6.6.5`` Pioneer obfuscated the ``app.asar`` file
|
208
|
-
contents, breaking the key extraction (see [this
|
203
|
+
contents, breaking the key extraction (see [this discussion](https://github.com/dylanljones/pyrekordbox/discussions/97) for more details).
|
209
204
|
If you are using a later version of Rekorbox and have no cached key from a previous
|
210
205
|
version, the database can not be unlocked automatically.
|
211
206
|
The command line interface of ``pyrekordbox`` provides a command for downloading
|
@@ -304,6 +299,8 @@ sync = mysett.get("sync")
|
|
304
299
|
quant = mysett.get("quantize")
|
305
300
|
````
|
306
301
|
|
302
|
+
The `DEVSETTING.DAT` file is still not supported
|
303
|
+
|
307
304
|
|
308
305
|
## 💡 File formats
|
309
306
|
|
@@ -323,6 +320,9 @@ If you encounter an issue or want to contribute to pyrekordbox, please feel free
|
|
323
320
|
`pyrekordbox` and the commit-message style can be found in
|
324
321
|
[CONTRIBUTING].
|
325
322
|
|
323
|
+
For general questions or discussions about Rekordbox, please use [GitHub Discussions][discussions]
|
324
|
+
instead of opening an issue.
|
325
|
+
|
326
326
|
Pyrekordbox is tested on Windows and MacOS, however some features can't be tested in
|
327
327
|
the CI setup since it requires a working Rekordbox installation.
|
328
328
|
|
@@ -374,6 +374,7 @@ the CI setup since it requires a working Rekordbox installation.
|
|
374
374
|
[mysettings-doc]: https://pyrekordbox.readthedocs.io/en/stable/formats/mysetting.html
|
375
375
|
|
376
376
|
[new-issue]: https://github.com/dylanljones/pyrekordbox/issues/new/choose
|
377
|
+
[discussions]: https://github.com/dylanljones/pyrekordbox/discussions
|
377
378
|
[CONTRIBUTING]: https://github.com/dylanljones/pyrekordbox/blob/master/CONTRIBUTING.md
|
378
379
|
[CHANGELOG]: https://github.com/dylanljones/pyrekordbox/blob/master/CHANGELOG.md
|
379
380
|
[INSTALLATION]: https://github.com/dylanljones/pyrekordbox/blob/master/INSTALLATION.md
|
@@ -0,0 +1,80 @@
|
|
1
|
+
docs/Makefile,sha256=4zv3TVkTACm6JBaKgTES3ZI9cETXgM6ULbZkXZP1as8,638
|
2
|
+
docs/make.bat,sha256=s8EuuVXNRnn4xmWLWTpk3Z01aqJSJT8ymrmK6ux0zbc,769
|
3
|
+
docs/source/api.md,sha256=IKKMeQ2wFSS_ippOWJ63ELtMjfkNe27KjWcFFEdUXnE,276
|
4
|
+
docs/source/conf.py,sha256=lPfAJtd0VY7uEDmHrEfHvQzNJg6WN_1seM9pCFc2e6E,6244
|
5
|
+
docs/source/index.md,sha256=_73xtwAH2OF0btxHt2Kre4k1cP3N-OhsyDL-Oc7NKCI,2823
|
6
|
+
docs/source/installation.md,sha256=RoCHv74LONL5QyBBVKXPOOqRmAN2GUzcvInwnlztM6M,40
|
7
|
+
docs/source/quickstart.md,sha256=-v7FE_08KDSUAdbmvrY-7QImKLpR8vA3W6mTFE-uWPs,6299
|
8
|
+
docs/source/requirements.txt,sha256=7YYc-tO0BQcBVUa4GvM1r1G5Ma9gkwGZpNbAKlJL97s,117
|
9
|
+
docs/source/_static/images/anlz_beat.svg,sha256=cVooi17HFw3mOT39cFNB05tr-cZwbK_Hjmho6eBJNX8,2924
|
10
|
+
docs/source/_static/images/anlz_file.svg,sha256=E74hjyTFJRy6tCxyTPfsYoM0GmOKoY1To_eHatpPEjs,12320
|
11
|
+
docs/source/_static/images/anlz_pco2.svg,sha256=LAv_EZf72FbslKdHmuBy5ROWy8xKKkkXqp7mSk3WG20,8155
|
12
|
+
docs/source/_static/images/anlz_pcob.svg,sha256=w4NRLCj0EMMnQ2i6tnlE-nFxETSiTs8PQF5nxMI3bgo,8682
|
13
|
+
docs/source/_static/images/anlz_pcp2.svg,sha256=JDncv7fo6ylorWyaH7kZ6roYB7zryFbJE_HY4IfWG8E,24904
|
14
|
+
docs/source/_static/images/anlz_pcpt.svg,sha256=8PcHBFAHXrDnTyC_7rO0TM-5cGy8BfjIXWxDi0zEK8Y,16473
|
15
|
+
docs/source/_static/images/anlz_ppth.svg,sha256=-rPlwvBrrsYaL87GNI-WJJBFCYkcZwkWoGwH5NT8bLk,7124
|
16
|
+
docs/source/_static/images/anlz_pqt2.svg,sha256=sRvbV-DjoTIWtxwChaw6kBqKFXNqBCF4V9hru9ZtdiE,18386
|
17
|
+
docs/source/_static/images/anlz_pqt2_2.svg,sha256=8g3XCJDz8xbH_KK7CvMpmIZjaGrWBKQVlA4k4M3PF50,13774
|
18
|
+
docs/source/_static/images/anlz_pqtz.svg,sha256=OPlW76hPkbhOByYgmVQfucTpb5irZXYcASZokfpK3A0,8141
|
19
|
+
docs/source/_static/images/anlz_pssi.svg,sha256=zM8fsNshSMd0wDvO_qM7RzsAKVIEN8Y41jY76hBJjyQ,11684
|
20
|
+
docs/source/_static/images/anlz_pssi_entry.svg,sha256=YkUg7fXtUst0OBI6vIlhHCK-nzhmR-22UGKFQ0pHxxA,12095
|
21
|
+
docs/source/_static/images/anlz_pvbr.svg,sha256=7mA6S__F4AoX-GamftlevellDiTlcIWRWi77A7HZEDg,7284
|
22
|
+
docs/source/_static/images/anlz_pwav.svg,sha256=psm3g7ILLVNgPNmPG2Hy7k9-q7qmnMnyQ3_-XYVAW2g,7566
|
23
|
+
docs/source/_static/images/anlz_pwv3.svg,sha256=FwZJIiHj5hj-FB-cioNlxrGBeIEuZy2BOLEzSWC57IE,8389
|
24
|
+
docs/source/_static/images/anlz_pwv4.svg,sha256=k5GQu2N965zPSG0FQWuml96UMfqBvs8Vy-Emklvn2dI,8173
|
25
|
+
docs/source/_static/images/anlz_pwv5.svg,sha256=Ph1TSlji2BgWK3rVI7MvnpGM68wef1s-Hs6OlmC0oQQ,8173
|
26
|
+
docs/source/_static/images/anlz_pwv5_entry.svg,sha256=TyNh_6gIsQKuDvS0RO5tmsptzmxiQ-LL1L3DQdIFi4I,5648
|
27
|
+
docs/source/_static/images/anlz_pwv6.svg,sha256=J2KvE_bI-NTFSpFp7QsKSsgbWGCyuUAuW_dOgdnv2DY,7577
|
28
|
+
docs/source/_static/images/anlz_pwv7.svg,sha256=n5NsTErvfsCw-otWdVXn12lM0tHyh-zQDZsZtQOJf0U,8173
|
29
|
+
docs/source/_static/images/anlz_pwvc.svg,sha256=wO_tsxRDVnAbptbSZYK1z7YOH-0vNUqXDB9Udj4ungA,7128
|
30
|
+
docs/source/_static/images/anlz_tag.svg,sha256=owPTtrTRDYCkaSg32_UJ-MTZp3K6WlkRQdJHTzdtxqs,6188
|
31
|
+
docs/source/_static/logos/dark/logo_primary.svg,sha256=pH4Q28u9JaCA4dVrKB-NDy00PNw2EhDwz7fIYUzezvk,3079
|
32
|
+
docs/source/_static/logos/light/logo_primary.svg,sha256=MUbYjowOcWdtoHVnK7pyV9xcTRdm7gS4whw94pVMGSI,3080
|
33
|
+
docs/source/_static/logos/mid/logo_primary.svg,sha256=utCWWbXXW5auRRposMMaMO9t_AflsN13rZGo3_Pg2WE,3078
|
34
|
+
docs/source/_templates/apidoc/module.rst_t,sha256=keQqb9G8LA-JS5tuytgzfsxRXsADEvuOz3mJjY9qYHU,185
|
35
|
+
docs/source/_templates/apidoc/package.rst_t,sha256=Ra-lunZcMyDtrb6vgrfAnBcBW4eFMTEIyg12fSe9h4o,1173
|
36
|
+
docs/source/_templates/apidoc/toc.rst_t,sha256=6EfpNwcRXLa8Ae3NcovuSmV5PSBy5gP0t6XfphUYb7M,127
|
37
|
+
docs/source/_templates/autosummary/class.rst,sha256=5yRMWkUNDXGvQE1gGm6yisL1bX4BDOsaBPciNUs_6vA,579
|
38
|
+
docs/source/_templates/autosummary/module.rst,sha256=xSCcWeBqQdMgpese2OwFlvzSOMsPZKTBCLCwgD3OG4k,789
|
39
|
+
docs/source/development/changes.md,sha256=ys1_zvYKQWEak_mBXBUMLpO3CWriHpjSAg1-17mTk04,40
|
40
|
+
docs/source/development/contributing.md,sha256=7bERN03kHo2Wf0ZrgHYP4MfBSdFGmE_Kk0CxK0uay2I,43
|
41
|
+
docs/source/formats/anlz.md,sha256=P7FrPqbu3i8SlRKVV9IU9cOQajKJHOjLlAWHheDGN7c,21455
|
42
|
+
docs/source/formats/db6.md,sha256=fuB1_PaalHV4zRkhXh5_mn1ZgHfPnSu_OzUOYbVls6w,30121
|
43
|
+
docs/source/formats/mysetting.md,sha256=OIYIn_B2HJcJtJM0wNjjzPy3xPkyz4raxg_-Y6E3D1k,9746
|
44
|
+
docs/source/formats/xml.md,sha256=uL7eWWQfQXsyf55Vydt1iLq7Jzio3kIGbSfjpg5-CRY,8491
|
45
|
+
docs/source/tutorial/anlz.md,sha256=JtcQIy44qaSP_GL0aL9VYQYOX8aVju7uFxgN3FPNy9o,145
|
46
|
+
docs/source/tutorial/configuration.md,sha256=mqmeFM3I7pDA_ajPQWqVyvc9Y0PWGYXLr3yyojTGsL8,2127
|
47
|
+
docs/source/tutorial/db6.md,sha256=wzfHAxSQ5rmYSDs6S0WheDwjkdFYsY3JxM0jGmXJcVU,6730
|
48
|
+
docs/source/tutorial/index.md,sha256=zjx5uXESYzHIyeqtL4XjhZUDPOpNiutZ3gkUOeG8R9w,318
|
49
|
+
docs/source/tutorial/mysetting.md,sha256=su-WMvEd3DrFF32RDfQBm3TlAUgK5PRPT1n_77BYnvY,3659
|
50
|
+
docs/source/tutorial/xml.md,sha256=x_rFvsVzZtUV0upEkPWkgrmr3B-FYjZKWvBuJBLFAu0,6414
|
51
|
+
pyrekordbox/__init__.py,sha256=jNxA9t2zA1P6zuOaQ9T_Nc5QW-G_KpUWymLNKOkPf7s,645
|
52
|
+
pyrekordbox/__main__.py,sha256=dm9QQMM5xPXzvFsF1ghwBor8ThUvZuC73As8c6kMZrA,6040
|
53
|
+
pyrekordbox/_version.py,sha256=RrHB9KG1O3GPm--rbTedqmZbdDrbgeRLXBmT4OBUqqI,411
|
54
|
+
pyrekordbox/config.py,sha256=eifT5Wejkk9zbQQ6190h-rF5WiF9foc55SNCHaYJKZk,21848
|
55
|
+
pyrekordbox/logger.py,sha256=qCY_3L_3WIMAvNmVKvh7oshGlWrd0T5aIW1bNCIu3Lo,521
|
56
|
+
pyrekordbox/utils.py,sha256=sT1xt-rM94Dir-S0OSSqKbquf9kdFbzZvBu3-7q0QDM,4361
|
57
|
+
pyrekordbox/xml.py,sha256=0qsKc8SQa54URwWGt6oHTOHBiAQU2S8MbSxDbzKUB7A,38369
|
58
|
+
pyrekordbox/anlz/__init__.py,sha256=OSpyl3pmmnt3mG905J9qvNnYchsgrIfathw0QXoOhjo,3156
|
59
|
+
pyrekordbox/anlz/file.py,sha256=i6Cnz-XpT47ClGhC3ZCCrgKhlk685NTM0hWyOuATNaE,6949
|
60
|
+
pyrekordbox/anlz/structs.py,sha256=Lt4fkb3SAE8w146eWeWGnpgRoP6jhLMWrSMoMwPjG04,7925
|
61
|
+
pyrekordbox/anlz/tags.py,sha256=uqneBP9CstYy7a4IpeEC9A5tfI59DQm0ik7yFXk_dNo,14094
|
62
|
+
pyrekordbox/db6/__init__.py,sha256=5XbfEIZuribNXIHMn9M4qw91cKHouH82PPJbmODhdgo,849
|
63
|
+
pyrekordbox/db6/aux_files.py,sha256=t0SfMCXBHjlCSfgRfpw1RTj0cmV0QY07xSLepjQqMnY,7591
|
64
|
+
pyrekordbox/db6/database.py,sha256=FlLeJgTp3-kt63_q5uod9Hzb5u83Z34Vhb9qI_qnYVM,74218
|
65
|
+
pyrekordbox/db6/registry.py,sha256=9GwMDKMqtm3-4nZ7mdzbL6XezPcVJUVqjHXGHKG_Jn4,9556
|
66
|
+
pyrekordbox/db6/tables.py,sha256=5ijJyO0OERbj1Usc__2QLRc7lPZ6FYluOXbajaEqzqk,65949
|
67
|
+
pyrekordbox/mysettings/__init__.py,sha256=rMS6Kknf1-X3PXF_TUxm8xui0H1Ap3R7G_9mqlnQUAM,705
|
68
|
+
pyrekordbox/mysettings/file.py,sha256=dQEsdBivpverXCssyVNUeQyUXIooNrOGIIiPLwi_6T4,12669
|
69
|
+
pyrekordbox/mysettings/structs.py,sha256=5Y1F3qTmsP1fRB39_BEHpQVxKx2DO9BytEuJUG_RNcY,8472
|
70
|
+
tests/__init__.py,sha256=s321RtRiHJsLt0YFq0NRncW7u66uwYHQE-6IjPcg10o,67
|
71
|
+
tests/test_anlz.py,sha256=hZAW5GFEJpU8UmmHYLuU5OZlApGrWZyPAR79KtaR0cw,5782
|
72
|
+
tests/test_config.py,sha256=wYp4wHRhIk2grhGXhk4z9umrhOWhVvTOm2mHydzLEs8,6157
|
73
|
+
tests/test_db6.py,sha256=6GUH_B1UAYfnJtjTK20cdCjP6hz_kDRR4KklvsnuAXg,37488
|
74
|
+
tests/test_mysetting.py,sha256=te6B_BErGc6ThgBpAl45n0Nd0hWJcm-X9WTw14bR6MA,5781
|
75
|
+
tests/test_xml.py,sha256=CbdzWgVjLwmxdaUqLo58QN4iRRaUdiJe_ni8y37BVfQ,16852
|
76
|
+
pyrekordbox-0.2.2.dist-info/LICENSE,sha256=Au8sngdQ79q5JsZXZJVU13j46VDxFbPwC4x6sSz8Jes,1074
|
77
|
+
pyrekordbox-0.2.2.dist-info/METADATA,sha256=jihtF6wZM3UKWuzrGLAxL-oOv4HjGyp-mwMbxdXmSB8,16672
|
78
|
+
pyrekordbox-0.2.2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
79
|
+
pyrekordbox-0.2.2.dist-info/top_level.txt,sha256=Cv8QDfcJ7y8fYUm0Q8D5GoiGxaqb7qt8Z5ntVbj1cLk,23
|
80
|
+
pyrekordbox-0.2.2.dist-info/RECORD,,
|