Transcode a Traktor playlist to MP3 files¶
This notebook exports a single Traktor playlist from a collection.nml and creates:
A output directory containing all audio files transcode into MP3 files
An
.m3uplaylist that references those files
Typical use case¶
Your Traktor collection was built on Windows (e.g. files live under
D:/SYNC/...)You are running this notebook on Linux, where the same drive is mounted somewhere like
/mnt/media/musicMultiple music formats need to be converted to mp3 for other applications
Prerequisites¶
Make sure you have ffmpeg installed and available on your PATH (ffmpeg -version works).
Setup¶
First we obtain our traktor .nml library collection which contains the playlists saved inside the traktor application.
from plistsync.services.traktor import NMLLibrary
# Path to you traktor collection
collection_path = "/Users/paul/Music/Traktor/collection.nml"
collection = NMLLibrary(collection_path)
We can quickly print all available playlists to get an overview of our library.
for pl in collection.playlists:
print(pl.name)
Next we select one playlist out of all playlist in the collection. For this change the name to the playlist you want to choose.
playlist = collection.get_playlist_or_raise(name="pl_name")
Let’s create a folder with the same name as the playlist to store the converted tracks.
from pathlib import Path
out_dir = Path(f"./{playlist.name}")
out_dir.mkdir(parents=True, exist_ok=True)
Converting NMLTracks to LocalTracks¶
NMLTrack/NMLPlaylistTrack objects are parsed from Traktor’s collection.nml and describe playlist tracks as Traktor stores them. LocalTrack represents the same track as a local filesystem Path, suitable for copying or transcoding.
In case you are running this on a machine with different
mount points or paths to the one defined in the traktor .nml library
you will need to apply a path rewrite the paths.
We expose the nifty PathRewrite class to help you with this.
from plistsync.core.rewrite import PathRewrite
# On windows we mount an external drive with music D:/Music
# While running the script on a linux machine the the music
# is now located in /mnt/media/music
rewrite = PathRewrite.from_str(old="/D:/SYNC", new="/mnt/media/music")
Converting to LocalTracks lets us verify the files exist and are readable/accessible before processing.
from plistsync.services.local import LocalTrack
from plistsync.services.traktor import NMLPlaylistTrack, NMLTrack
def to_local_track(track: NMLPlaylistTrack | NMLTrack) -> LocalTrack:
"""Convert a NMLTrack to a local track."""
local_path = rewrite.apply(track.path)
return LocalTrack(
path=local_path,
)
local_tracks = [to_local_track(t) for t in pl.tracks]
Not all local tracks are already MP3, and sometimes you need a specific format for playback. We use ffmpeg to transcode tracks when needed before writing them to the output directory.
import subprocess
def _transcode_to_mp3(input_path: Path, output_path: Path) -> None:
"""
Transcode an audio file to MP3 using the system `ffmpeg` binary.
Notes
-----
Requires `ffmpeg` to be installed and available on PATH.
Metadata is copied from the input and ID3v2.3 tags are written for
broad compatibility.
"""
output_path.parent.mkdir(parents=True, exist_ok=True)
if output_path.exists():
return
# fmt: off
cmd = [
"ffmpeg", "-hide_banner", "-loglevel", "error", "-n",
"-i", str(input_path),
"-map_metadata", "0", # Copy metadata
"-id3v2_version", "3", # ID3v2.3
"-vn", # Drop video stream
"-codec:a", "libmp3lame", # encoder
"-b:a", "320k", # bitrate
str(output_path),
]
subprocess.run(cmd, check=True)
# fmt: on
def to_mp3_local_track(track: LocalTrack, out_dir: Path) -> LocalTrack:
"""
Ensure `track` is available as an MP3 inside `out_dir`.
- If the input is already an MP3, it is copied (no re-encoding).
- Otherwise, it is transcoded to MP3 via `_transcode_to_mp3()`.
Returns a `LocalTrack` pointing at the MP3 in `out_dir`.
"""
# Copy if already mp3
if track.path.suffix.lower() == ".mp3":
out_path = out_dir / track.path.name
if not out_path.exists():
shutil.copy2(track.path, out_path)
return LocalTrack(path=out_path)
# Transcode if not mp3
out_path = out_dir / track.path.with_suffix(".mp3").name
_transcode_to_mp3(track.path, out_path)
return LocalTrack(path=out_path)
We now copy tracks into the output directory, transcoding to MP3 only when needed.
mp3_tracks = [to_mp3_local_track(t, out_dir) for t in local_tracks]
Creating the M3U playlist¶
Lastly, we write an .m3u playlist file into the same output folder. It is a simple text format with one track path per line.
m3u_path = out_dir / f"{playlist.name}.m3u"
with m3u_path.open("w") as f:
for track in mp3_tracks:
f.write(f"./{track.path.relative_to(out_dir)}\n")