Skip to content

Instantly share code, notes, and snippets.

@elliotchance
Last active November 9, 2025 10:09
Show Gist options
  • Save elliotchance/86ff315d552df269e97ff52b129e02a7 to your computer and use it in GitHub Desktop.
Save elliotchance/86ff315d552df269e97ff52b129e02a7 to your computer and use it in GitHub Desktop.
Download cue file from 1001tracklists.com

Usage

  1. Goto any tracklist page
  2. View Menu > Developer Tools
  3. Paste this entire cue.js and hit enter. It may prompt to allow downloads.
  4. The mp3 file must be in the same folder AND named the same as the cue file (without extension).

For example:

2025-09-25 Falden - Colorcast 246.cue
2025-09-25 Falden - Colorcast 246.mp3

Other Notes

  1. Not all tracks have a start time. If they do not, it will try to guess by their placement. However, this relies on at least one player being attached to know the total length.
  2. I have tested this with Chrome v141 and Safari v17.4.1.
const cue = [];
const pageTitle = $("div#pageTitle").text().trim();
const parts = pageTitle
.split(/ [@-] /, 2)
.map((p) => p.trim());
const date = parts[1].substr(parts[1].length - 10);
const title = parts[1].substr(0, parts[1].length - 10).trim();
const filename = `${date} ${pageTitle.substr(0, pageTitle.length-10).trim()}`;
cue.push(
`REM Generated by https://gist.github.com/elliotchance/86ff315d552df269e97ff52b129e02a7`,
`PERFORMER "${parts[0]}"`,
`TITLE "${date} ${title}"`,
`FILE "${filename}.mp3" MP3`
);
function parseTimeToSeconds(timeStr) {
const parts = timeStr.split(':').map(Number);
if (parts.length === 3) {
const [hours, minutes, seconds] = parts;
return hours * 3600 + minutes * 60 + seconds;
} else if (parts.length === 2) {
const [minutes, seconds] = parts;
return minutes * 60 + seconds;
} else {
throw new Error(`Invalid time format "${timeStr}"`);
}
}
const totalDuration = $('li.tBtn').text().match(/\[((\d+:)?(\d+):(\d+))\]/) ||
['', prompt("Duration is unknown. How long is the mix? (MM:SS)")];
const trackStarts = [];
$("div.bItm").each((i, t) => {
const text = $(t).text();
if (text.includes('correct cue time is')) {
const startTime = $(t).find('.italic').text().trim();
trackStarts[trackStarts.length-1] = parseTimeToSeconds(startTime);
} else if (!text.match(/^\s*\d+\s+/)) {
// Avoid (trimmed) - these are not tracks:
// 1:16:31Estiva - Peppa[25-10-22 19:04:00]akselek
// w/ 1:50:38 Estiva - Via Infinita COLORIZE (ENHANCED)
// track wasn't played[25-10-20 23:34:30]biscram[poll:0/1/0]
return
} else {
const startTime = $(t).find(".cue:last-of-type").text();
trackStarts.push(startTime ? parseTimeToSeconds(startTime) : 0);
}
})
function fillMissingTimes(times, maxTime) {
const result = [...times];
let lastKnownTime = 0;
let lastKnownIndex = 0;
// Collect indices of known (non-zero) times
const known = times
.map((t, i) => (t > 0 ? [i, t] : null))
.filter(Boolean);
// If no known times, evenly distribute
if (known.length === 0) {
const step = Math.floor(maxTime / times.length);
return times.map((_, i) => i * step);
}
// Fill between known times
for (let k = 0; k < known.length; k++) {
const [i, t] = known[k];
const gap = i - lastKnownIndex;
if (gap > 1) {
const step = (t - lastKnownTime) / gap;
for (let j = 1; j < gap; j++) {
result[lastKnownIndex + j] = Math.round(lastKnownTime + step * j);
}
}
lastKnownTime = t;
lastKnownIndex = i;
}
// Fill after the last known time up to maxTime
if (lastKnownIndex < times.length - 1) {
const remaining = times.length - lastKnownIndex - 1;
const step = (maxTime - lastKnownTime) / (remaining + 1);
for (let j = 1; j <= remaining; j++) {
result[lastKnownIndex + j] = Math.round(lastKnownTime + step * j);
}
}
return result;
}
const fixedTrackStarts = fillMissingTimes(trackStarts, parseTimeToSeconds(totalDuration[1]));
let trackNumber = 1;
$("div.bItm.tlpItem").each((_, t) => {
if (!$(t).text().match(/^\s*\d+\s+/)) {
return
}
const parts = (
$(t).find("meta[itemprop=name]").attr("content") || $(t).find('span.trackValue').text()
).split(" - ", 2);
const h = Math.floor(fixedTrackStarts[trackNumber-1] / 60);
const m = fixedTrackStarts[i] % 60;
const d = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
cue.push(
` TRACK ${String(trackNumber).padStart(2, "0")} AUDIO`,
` PERFORMER "${parts[0].trim()}"`,
` TITLE "${parts[1].trim()}"`,
` INDEX 01 ${d}:00`,
);
++trackNumber;
});
(() => {
const content = cue.join("\n") + "\n";
console.log(content);
const url = URL.createObjectURL(new Blob([content], { type: "application/x-cue" }))
const a = document.createElement("a");
a.href = url;
a.download = `${filename}.cue`;
setTimeout(() => a.click(), 0); // ensures Chrome treats it as user action
setTimeout(() => URL.revokeObjectURL(url), 1000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment