Contents
- Includes
- Example uses
- Installation
- Examples
- Analyzing and setting a dynamic beatgrid
- Exporting crates to USB
- Renaming a file and changing its location in the database
- Modifying the database file
- Setting track color
- Snapping cue positions to the beatgrid
- Modifying track metadata / hot cues
- Listing available crates or smart crates
- Modifying a SmartCrate Rule
- Crate details and adding a track
- Smart Crate details
- Serato Tags
- Sources
- Contributing
Includes:
- Serato track tag parsing and modification, including cue points, track color, beatgrid, waveform, autogain, etc.
- Serato library database parsing and modification
- Serato crate parsing and modification
- Serato smart crate parsing and rule modification
- Dynamic beatgrid analysis that can be saved to a track's beatgrid.
- Serato USB export that beats Serato's sync by putting all files in 1 folder (without duplicates) and only copying changed files, unlike Serato's sync which takes forever and creates many duplicate file locations
Currently designed for Python 3.12+. If you would like backwards compatibility with an older version, please reach out!
Example uses:
USB:
- Export crates (including Smart Crates) to USB. Beats Serato's sync by keeping all files in 1 folder and by comparing files for changes (instead of always copying, like Serato does)
Tracks:
- Change hot cue's text (i.e. change to all caps; change "c" to "CHORUS", etc.)
- Set a hot cue's text based on its color (i.e. if RED, set to "EXIT")
- Set a track's color (i.e. if has hot cues, change to BLUE)
- Set a piece of metadata due to a track's color (i.e. if track is green, set "grouping" to "TAGGED") (this is useful since can't create smart crates by track color in Serato)
- Analyze a track's Dynamic Beatgrid and save it to the beatgrid Serato tag.
- Snap cue points to the nearest beat with a configurable tolerance (e.g. 1/16, 1/8, 1/4 of a beat).
Database:
- Rename a file while changing its location in the database as well, so that it doesn't go missing.
- After changing a track's metadata, modify database values for a specific track so that you don't have to "Reload Id3 Tags" in Serato, the change appears in Serato instantly.
Crate:
- Read a crate's tracks
- Remove a track from a crate
- Add a track to a crate
- List available crates
Smart Crate:
- Read a smart crate's tracks and rules
- Modify a smart crate's rules
- List available smart crates
Installation
pip install serato-tools
The following deps are optional, but must be installed based on what features you want to use (each is also available as a package extra, e.g. pip install "serato-tools[track_tags]"):
- For getting/modifying track metadata including tags, cue points, beatgrid, and waveform, must install
pip install mutagen(extra:track_tags) - For viewing a track's waveform, must install
pip install pillow(extra:waveform_drawing) - For beatgrid analysis, must install
pip install numpyandpip install librosa(extra:beatgrid_analysis)
Examples
Analyzing and setting a dynamic beatgrid
Rekordbox has Dynamic Beatgrid Analysis but Serato doesn't. This analyzes a non-consistent BPM across a track, such as a track with live drums, and snaps a beatgrid marker to each beat.
NOTE: This feature has only been tested on a couple of tracks. Recommend reviewing the resulting beatgrid in Serato— some grid markers may require adjustment. It seems to be working pretty great though!
>>> serato_analyze_beatgrid "/Users/Username/Music/Dubstep/Raaket - ILL.mp3"
Exporting crates to USB
NOTE: replaces existing crates on flash drive! (but does not delete existing track files) (TODO: ability to merge with existing)
>>> serato_usb_export --drive E --crate_matcher "*house*" "*techno*" --root_crate="Dave USB"
Renaming a file and changing its location in the database
This renames the file path, and also changes the path in the database to point to the new filename, so that the renamed file is not missing in the library.
NOTE: Recommended to make a backup of the database file elsewhere, before modifying via this package, in case an unforseen bug appears.
from serato_tools.database_v2 import DatabaseV2
db = DatabaseV2()
db.rename_track_file(
src="/Users/Username/Music/Dubstep/Raaket - ILL - Original Mix.mp3",
dest="/Users/Username/Music/Dubstep/Raaket - ILL.mp3",
)
Modifying the database file
NOTE: Recommended to make a backup of the database file elsewhere, before modifying via this package, in case an unforseen bug appears.
import time
from typing import Any
from serato_tools.database_v2 import DatabaseV2
now = int(time.time())
files_set_date = ["/Users/Username/Music/Dubstep/Raaket - ILL.mp3"]
def modify_uadd(filename: str, prev_val: Any):
print(f'Serato library change - Changed "date added" to today: {filename}')
return now
def modify_tadd(filename: str, prev_val: Any):
return str(now)
def remove_group(filename: str, prev_val: Any):
return " "
db = DatabaseV2()
# the available fields are in DatabaseV2.Fields
db.modify_and_save(
rules=[
{"field": DatabaseV2.Fields.DATE_ADDED_U, "files": files_set_date, "func": modify_uadd},
{"field": DatabaseV2.Fields.DATE_ADDED_T, "files": files_set_date, "func": modify_tadd},
{"field": DatabaseV2.Fields.GROUPING, "func": remove_group}, # all files
]
)
Setting track color
from serato_tools.track_cues_v2 import TrackCuesV2
tags = TrackCuesV2('/Users/Username/Music/Dubstep/Raaket - ILL.mp3')
tags.set_track_color(
TrackCuesV2.TrackColors.PURPLE,
delete_tags_v1=True
# delete_tags_v1 (the default) deletes the legacy "Serato Markers_" tag, since a stale one stops the new color from appearing in Serato. No issues have been found from deleting it, but use caution with versions of Serato before 3, which may still read it.
)
tags.save()
Snapping cue positions to the beatgrid
After you have a good beatgrid (including dynamic beatgrids), you can snap cue positions so they land exactly on beats, within a configurable tolerance:
serato_snap_cues_v2 "/Users/Username/Music/Dubstep/Raaket - ILL.mp3" --snap_cues --snap_tolerance 1/16
--snap_cuesenables snapping for all cue entries in theSerato Markers2tag.--snap_toleranceis a fraction of a beat (e.g.1/16,1/8,1/4) that a cue point can come within a beat in order to "snap" to it.
Only cue points within the tolerance window of some beat are moved; others are left unchanged.
Modifying track metadata / hot cues
import dataclasses
from mutagen.id3._frames import TIT1
from serato_tools.track_cues_v2 import TrackCuesV2
CueColors = TrackCuesV2.CueColors
TrackColors = TrackCuesV2.TrackColors
tags = TrackCuesV2('/Users/Username/Music/Dubstep/Raaket - ILL.mp3')
def track_rule(track: TrackCuesV2.TrackCuesInfo) -> TrackCuesV2.TrackCuesInfo | None:
# Set ID3 grouping field from track color
if track.color is not None:
track_color = track.color.color
if track_color == TrackColors.LIMEGREEN3.value:
tags.tagfile.tags.setall("TIT1", [TIT1(text="TAGGED")])
elif track_color in [TrackColors.WHITE.value, TrackColors.GREY.value, TrackColors.BLACK.value]:
tags.tagfile.tags.setall("TIT1", [TIT1(text="UNTAGGED")])
# Normalize cue colors and names
new_cues = []
for c in track.cues:
# Make "close to red", red.
cue_color = CueColors.RED.value if c.color in [CueColors.PINKRED.value, CueColors.MAGENTA.value] else c.color
# Make all cuenames all-caps
cue_name = c.name.strip().upper()
new_cues.append(dataclasses.replace(c, color=cue_color, name=cue_name))
new_track = dataclasses.replace(track, cues=new_cues)
return new_track if new_track != track else None
tags.modify_entries(track_rule, delete_tags_v1=True)
tags.save()
Listing available crates or smart crates
from serato_tools.crate import Crate
print('\n'.join(Crate.get_crate_files()))
from serato_tools.smart_crate import SmartCrate
print('\n'.join(SmartCrate.get_crate_files()))
Modifying a SmartCrate Rule
via command line
Each rule takes a comparison and a value (or DELETE to remove the rule). The available comparisons are the SmartCrate.RuleComparison names, lowercase: str_contains, str_is, int_is_ge, etc.
one crate
>>> serato_smartcrate '/Users/Username/Music/_Serato_/SmartCrates/Dubstep.scrate' --set_rules --grouping str_is UNTAGGED
all crates
>>> serato_smartcrate --all --set_rules --grouping str_is UNTAGGED
via code
from serato_tools.smart_crate import SmartCrate
crate = SmartCrate('/Users/Username/Music/_Serato_/SmartCrates/Dubstep.scrate')
# updates the rule on this field, or adds the rule if the crate doesn't have one for it.
# the value is stored in the value field the comparison expects, just like Serato does.
crate.set_rule(SmartCrate.RuleField.GROUPING, SmartCrate.RuleComparison.STR_IS, "UNTAGGED")
crate.save()
Crate details and adding a track
from serato_tools.crate import Crate
crate = Crate('/Users/Username/Music/_Serato_/Subcrates/Dubstep.crate')
crate.print_track_paths()
# /Users/Username/Music/Dubstep/Saka - backitup.mp3
# /Users/Username/Music/Dubstep/Mind Splitter - YAPPIN'.mp3
# /Users/Username/Music/Dubstep/Flozone - DO IT.mp3
# /Users/Username/Music/Dubstep/Evalution - Throw It Back.mp3
# ...
print(crate) # or crate.print()
# vrsn (Version): 1.0/Serato ScratchLive Crate
# osrt (Sorting): [ tvcn (Column Name): key ], [ brev (Reverse Order): False ]
# ovct (Column): [ tvcn (Column Name): song ], [ tvcw (Column Width): 0 ]
# ovct (Column): [ tvcn (Column Name): artist ], [ tvcw (Column Width): 0 ]
# ovct (Column): [ tvcn (Column Name): bpm ], [ tvcw (Column Width): 0 ]
# ovct (Column): [ tvcn (Column Name): playCount ], [ tvcw (Column Width): 0 ]
# otrk (Track): [ ptrk (Track Path): Music/Dubstep/Flozone - Candy Paint.mp3 ]
# otrk (Track): [ ptrk (Track Path): Music/Dubstep/Mind Splitter - LISTEN TO ME.mp3 ]
# otrk (Track): [ ptrk (Track Path): Music/Dubstep/Flozone - DO IT.mp3 ]
# ...
# Example: Add tracks to the crate and save it as a new crate
crate.add_tracks(['/Users/Username/Music/Dubstep/Raaket - ILL.mp3'])
crate.save('/Users/Username/Music/_Serato_/Subcrates/New Crate.crate')
Smart Crate details
from serato_tools.smart_crate import SmartCrate
s_crate = SmartCrate('/Users/Username/Music/_Serato_/SmartCrates/Dubstep.scrate')
print(s_crate)
Serato Tags
Original writeup on Serato GEOB tag discoveries: blog post
| GEOB Tag | Research Progress | Contents | Script File |
|---|---|---|---|
Serato Analysis |
Done | Serato version information | |
Serato Autotags |
Done | BPM and Gain values | track_autotags.py |
Serato BeatGrid |
Mostly done | Beatgrid Markers | track_beatgrid.py |
Serato Markers2 |
Mostly done | Hotcues, Saved Loops, etc. (The main one used in newer versions of Serato) |
track_cues_v2.py |
Serato Markers_ |
Mostly done | Hotcues, Saved Loops, etc. (Old, not used in newer versions of Serato) |
track_cues_v1.py |
Serato Offsets_ |
Not started | ??? | |
Serato Overview |
Done | Waveform data | track_waveform.py |
The different file/tag formats that Serato uses to store the information are documented in docs/fileformats.md, a script to dump the tag data can be found at track_tagdump.py.
Sources
- Serato track file tag parsing and modification from https://github.com/Holzhaus/serato-tags , which appears to be no longer maintained
- Serato crate parsing and modification from https://github.com/sharst/seratopy
- Dynamic beatgrid analysis from https://github.com/heyitsmass/audio
Contributing
If you want a new feature, or have a bug fix, please put in a PR!
Metadata
Release files for serato-tools 5.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| serato_tools-5.1.2.tar.gz | 51.3 kB | With provenance | Details for serato_tools-5.1.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| serato_tools-5.1.2-py3-none-any.whl | Python 3 | none | any | Details for serato_tools-5.1.2-py3-none-any.whl |
Total release size: 101.8 kB
Release files / serato_tools-5.1.2.tar.gz
| Download URL | serato_tools-5.1.2.tar.gz |
|---|---|
| Size | 51.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
103617a0949877092df7dcad2201d7a9338ecf93689fd51e6af4794b30d88948
|
|
BLAKE2b-256 checksum How to use checksums |
98d2a26f0630331060ce616a3b8451ba3690430024e9e5f2bd11f1e294138eb6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.
Transparency logRelease files / serato_tools-5.1.2-py3-none-any.whl
| Download URL | serato_tools-5.1.2-py3-none-any.whl |
|---|---|
| Size | 50.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8640724c4d1f9455b4d0d4e6ba1887f08bd5c9531330bea64dfddc025f635e1f
|
|
BLAKE2b-256 checksum How to use checksums |
8219832e83f35be142b25088cb57fa0bd38066d8b7676dd105ebb3be4ec05a54
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.
Transparency logSecurity for serato-tools 5.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for serato_tools-5.1.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.