Skip to main content

Playlistparser

Tool for parsing DJ software playlists. Currently supports Engine DJ, Rekordbox, Serato, Traktor, and VirtualDJ. Part of Slipmat.io music tools.

Free hosted version of this tool: https://slipmat.io/tools/playlistconverter/

Installing

uv add playlistparser

Command line

uvx playlistparser parse myplaylist.nml
1. Technotronic - Pump Up The Jam (Edit)
2. Guru Josh - Infinity (1990s... Time for the Guru 12" Mix)

Rekordbox · 2 tracks

--json gives every field of every track:

{
  "tracks": [
    {
      "title": "Pump Up The Jam (Edit)",
      "artist": "Technotronic",
      "album": "",
      "key": "",
      "duration": 216,
      "year": 1989,
      "bpm": 124.0,
      "file_path": "",
      "vendor_id": ""
    }
  ],
  "summary": { "playlist_type": "REKORDBOX", "track_count": 1 }
}

Exit code is 1 if the file can't be parsed.

Usage

One end-to-end example covering format detection, streaming, required fields, aggregates, per-track data and every exception you need to handle:

import logging

from playlistparser import (
    MalformedPlaylistError,
    MissingFieldError,
    PlaylistParser,
    PlaylistParserError,
    PlaylistType,
    UnknownFormatError,
)

logging.basicConfig(level=logging.INFO)

try:
    # Construct a parser. All keyword arguments are optional.
    #
    #   require        — fail fast if any listed field is missing on a row.
    #                    If the format itself can't expose the field (e.g. Serato
    #                    has no bpm), MissingFieldError is raised before track
    #                    parsing. CSV detection reads the header first.
    #   as_type        — override format detection (use for unusual file
    #                    extensions); otherwise the format is detected from the
    #                    extension, and for .csv from the header row.
    #   default_artist — substituted when a row has no artist field.
    #
    # Recoverable per-row warnings are emitted via stdlib `logging` under the
    # `playlistparser.parsers.*` logger names — configure logging at the root
    # (or route through structlog with `structlog.stdlib.LoggerFactory()`).
    pl = PlaylistParser(
        "history.csv",
        require=["title", "artist"],
        default_artist="Unknown Artist",
        # as_type=PlaylistType.ENGINE,  # uncomment to bypass detection
    )

    # Detected format (PlaylistType enum: ENGINE, REKORDBOX, SERATO,
    # TRAKTOR, VIRTUALDJ). For .csv this triggers a one-time header sniff.
    print(f"Format: {pl.playlist_type.name}")

    # Stream tracks. Iteration is lazy — each pass re-reads the file unless
    # you materialise with .to_list() (cached for the lifetime of the parser).
    for track in pl:
        # str(track) → "Artist - Title"
        print(track)

        # Track is a frozen dataclass with these fields (all always present;
        # unsupported / missing values are 0 or ""):
        #   title: str, artist: str, album: str, key: str
        #   duration: int (seconds), year: int, bpm: float
        #   file_path: str, vendor_id: str
        print(track.bpm, track.year, track.duration_str())  # e.g. "128.0 2024 6:42"

        # Serialise for JSON / DB. no_meta=True keeps only title + artist.
        payload = track.as_dict()

    # Aggregates materialise the full list once and cache it.
    print(f"{pl.track_count} tracks, {pl.total_duration}s total")

    # Explicit materialisation if you need the list directly.
    tracks = pl.to_list()

except UnknownFormatError as e:
    # Extension not recognised, or CSV header didn't match any known format.
    # Pass as_type=PlaylistType.X to override.
    print(f"Unsupported file: {e}")

except MissingFieldError as e:
    # A required field was missing — either unsupported by the format
    # (raised before parsing) or absent on a specific row.
    # e.field, e.line, e.track_title are available for diagnostics.
    print(f"Missing '{e.field}' on line {e.line}: {e.track_title!r}")

except MalformedPlaylistError as e:
    # Structural problem with the file (bad XML, truncated row, etc).
    # e.path and e.line locate the problem.
    print(f"Corrupt playlist: {e}")

except PlaylistParserError as e:
    # Base class — catch this if you don't care which of the above fired.
    print(f"Could not parse playlist: {e}")

except FileNotFoundError:
    # The library does not check existence in the constructor; the file is
    # opened on the first iteration / aggregate access.
    print("Playlist file does not exist")

Parsing progress

Pass a callback to stream() when displaying progress:

def report_progress(tracks_done, total_tracks, bytes_read, bytes_total):
    byte_percent = bytes_read / bytes_total if bytes_total else 1
    print(tracks_done, total_tracks, byte_percent)


for track in PlaylistParser("set.nml").stream(on_progress=report_progress):
    save(track)

Byte progress is monotonic and completes even when malformed source records are skipped. When available, track totals count source records, so tracks_done can finish below total_tracks when records are skipped. total_tracks is None when the source has no valid count, such as a Traktor collection without a valid ENTRIES value. Delimited formats count logical records in a pre-pass.

Supported formats and fields

Format PlaylistType Extension
Engine DJ ENGINE .csv
Rekordbox REKORDBOX .txt
Serato SERATO .csv
Traktor TRAKTOR .nml
VirtualDJ VIRTUALDJ .csv

CSV formats are detected by sniffing the header row.

BPM is a float rounded to one decimal place. Traktor's vendor_id is its ENTRY.AUDIO_ID.

Format title artist album key duration year bpm file_path vendor_id
Engine DJ x x x x x x x
Rekordbox x x x x x x x x
Serato x x x
Traktor x x x x x x x x x
VirtualDJ x x x x x x

Developing

  • uv run ruff format - format
  • uv run ruff check --fix --extend-fixable F401 - lint
  • uv run ty check - typecheck
  • uv run pytest - run test suite

Contributing

Contributions are welcome! Please follow the code of conduct when interacting with others.

Elsewhere

Licence

Copyright © 2022, Ville Säävuori. Released under the GNU Affero General Public License v3.0.

Commercial licenses are also available.

Release files for playlistparser 4.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for playlistparser 4.3.0
File Size Uploaded
playlistparser-4.3.0.tar.gz 15.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for playlistparser 4.3.0
File Interpreter ABI Platform
playlistparser-4.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.9 kB

Release files / playlistparser-4.3.0.tar.gz

Download URL playlistparser-4.3.0.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
18f99f215f2b2b29b57c1242fe3cc8f77237f44380204d527f4f8cce704179f5
BLAKE2b-256 checksum
How to use checksums
bac7e24c4a7ad179f8123fba8c22642b8441339b34efc12e2ac4f9ba9a414c81
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / playlistparser-4.3.0-py3-none-any.whl

Download URL playlistparser-4.3.0-py3-none-any.whl
Size 19.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
127e7dea5cd36712cbec68f0d9bd9ab5c397e7aa7f0d1caeed70246888f78d6c
BLAKE2b-256 checksum
How to use checksums
79891b801003d6214f115205330d432cf231324be09d976c38fc159131e79fd3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page