Repository navigation
python: v0.3.1
0.3.1 is the first published release with tracklists. 0.3.0 was tagged, but its publish jobs failed on a test fixture that pinned the spec version, so it never reached npm or PyPI. The code is the same.
trackparse now reads whole tracklists. Paste the tracklist of a mix or a podcast and you get every line back parsed, with its start time, plus a list of problems with line numbers. Edits change only the lines they name. Both packages have it: trackparse/tracklist on npm and trackparse.tracklist on PyPI.
import { applyEdits, parseTracklist } from "trackparse/tracklist";
const text = [
"48:20 Noisia - Stigma",
"52:47 Mefjus & Kasra - Bandwagon (Phace Remix)",
"56:05 Black Sun Empire - ID",
"01:07:44 Imanu - Sacrifice",
"00:11:02 Current Value feat. Teddy Killerz - Payload",
"01:15:36 Hybris - Spinning Top",
].join("\r\n");
const list = parseTracklist(text);
list.entries[1].track.versions[0].type; // 'remix'
list.warnings.find((w) => w.code === "time-out-of-order");
// { code: 'time-out-of-order', line: 5, severity: 'warning',
// suggestion: { line: 5, raw: '01:11:02', seconds: 4262 } }
applyEdits(text, [{ op: "shift", seconds: 45, from: 2 }]).changed; // [ 2, 3, 4, 5, 6 ]Tracklists
- Lossless. Every line keeps its exact text and its own line ending (LF, CRLF or CR), so joining the entries gives back the input byte for byte. Each line is also cut into
lead,time,gapandrest, which join back into the line. - Per line: a kind (
track,marker,header,link,note,blank), the start time in seconds (12:34,1:02:03,125:30,00:00:07.500, in brackets or not, followed by-,|,=>and friends), and a fullParsedTrackfor track and marker lines. - Checks with line numbers: times out of order, duplicated or missing, no start at zero, past the end of the audio, invalid times like
1:75, gaps and duplicates in track numbers, and lines with no artist/title separator. An out-of-order time comes with a suggested fix when exactly one time one or two digits away fits, sometimes on the previous line, where the typo really was. - Edits on the text:
set-time,shift(one line, some, or everything from a line on),sort-by-time,set-rest,insert,delete. Other lines keep their bytes. A rewritten time keeps its line's style (52:47staysMM:SS,00:52:47staysHH:MM:SS), and a time that crosses the hour gains hours the way the rest of the list writes them. Invalid edits change nothing and warn; nothing throws. - Bare track numbers (
8 Linkin Park_-_Points Of Authority) count as positions when the neighbouring lines continue the sequence, so2 Unlimitedstill stays an artist. - A separate entry. Single-track users don't load it. It adds about 7.6 KiB gzipped, with an 8 KiB budget checked in CI.
Checked on real tracklists
The rules were written against all 117 tracklists of the Neuropunk podcast, 5,031 lines:
- every line round-trips byte for byte;
- every line's time matches what the platform's current player regex reads;
- all 81 bare track numbers in the one list written without dots are found;
- all 10 real out-of-order times are flagged, and the 2 that have a unique fix get it;
- only 2 lines stay unparsed, and neither has a separator at all.
The corpus isn't redistributable, so it stays out of the repo. scripts/corpus-check.mjs re-runs these checks for anyone who has it, and every interesting line from it is now a fixture.
mode: "tracklist" for parse()
The mode each tracklist line is parsed with is available on its own. The first spaced dash always separates artist and title, so lines that single-track parsing reads differently come out the way a tracklist means them:
parse("Electrosoul System - Liquid Metal").junk; // [ { raw: 'Liquid Metal', kind: 'genre' } ]
parse("Electrosoul System - Liquid Metal", { mode: "tracklist" }).title; // 'Liquid Metal'Default single-track parsing is unchanged: no existing fixture expectation moved.
Spec and tests
- New SPEC section R13 (tracklists) and R6.10 (tracklist mode), with
tracklist.schema.json. - 1,074 new fixtures (248 handwritten, 826 generated), 12,965 in total. JS and Python pass all of them, and
compare-portsfinds 0 differences in the full output, edits included. - New property tests in both ports: lossless split, edit locality, shift round trip, and linear time on 10,000 lines.
Changelog
0.3.1 (2026-10-12)
Bug Fixes
- fixtures: stop pinning specVersion in tracklist fixtures (6998e00)
Install (Python)
pip install trackparse==0.3.1PyPI: https://pypi.org/project/trackparse/0.3.1/ · implements spec 0.3.1 (SPEC.md)