Skip to content

Instantly share code, notes, and snippets.

@thde
Last active December 26, 2024 17:18
Show Gist options
  • Save thde/12c8e6f692c6fe6dbc38b37ef27dfa2b to your computer and use it in GitHub Desktop.
Save thde/12c8e6f692c6fe6dbc38b37ef27dfa2b to your computer and use it in GitHub Desktop.
// @name Bandcamp Purchase Export
// @description Allows to export Bandcamp purchases as a CSV file.
const main = async () => {
const currentRates = rates("CHF");
await loadAllPurchases();
const allPurchases = purchases(await currentRates);
if (allPurchases.length == 0) {
alert(
"No purchase has been found on the current page!\nEnsure you're on Bandcamp's purchases page, before running this bookmarklet.",
);
return;
}
download("bandcamp-purchases.csv", convertToCSV(allPurchases));
};
const loadAllPurchases = async (timeoutMS = 1000) => {
const allButton = document.querySelector(".view-all-button");
if (allButton != null) {
allButton.click();
await sleep(timeoutMS);
}
// scroll to bottom of page to trigger loading of additional purchases
while (window.innerHeight + window.scrollY < document.body.offsetHeight - 2) {
window.scrollTo(0, document.body.scrollHeight, { behavior: "smooth" });
await sleep(timeoutMS);
}
};
const purchases = (rates) =>
[...document.querySelectorAll(".purchases-item")].map((e) => {
const s = e
.querySelector(".purchases-item-total strong:last-child")
.innerText.split(" ");
const purchaseDate = new Date(
e.querySelector(".purchases-item-date").innerText.trim(),
);
const currency = s[1].trim();
const amount = parseFloat(s[0].replace(/[^\d.-]/g, ""));
return {
date: purchaseDate.toLocaleDateString("de-CH"),
currency: rates.base,
amount:
currency == rates.base
? amount
: (amount * rates.rates[currency]).toFixed(2),
originalCurrency: currency,
originalAmount: amount,
title: e.querySelector(".purchases-item-title").innerText.trim(),
};
});
const convertToCSV = (elements, separator = ",") =>
[Object.keys(elements[0]), ...elements.map((line) => Object.values(line))]
.map((line) =>
line
.map((element) => {
if (typeof element !== "string") return element;
return `"` + element.replaceAll(`"`, `""`) + `"`;
})
.join(separator),
)
.join("\n");
const download = (filename, text) => {
const element = document.createElement("a");
element.setAttribute(
"href",
"data:text/csv;charset=utf-8," + encodeURIComponent(text),
);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const rates = async (base) => {
const resp = await fetch(`https://api.frankfurter.app/latest?from=${base}`);
return await resp.json();
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment