Import/export your Brainscape decks from/into csv files without a Pro account.
// ==UserScript==
// @name Brainscape Bene
// @author LucunJi
// @version 1.1.0
// @match https://www.brainscape.com/l/dashboard/*/decks*
// @grant none
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js
// @license MIT
// @description Import/export your Brainscape decks from/into csv files without a Pro account.
// @namespace https://github.com/LucunJi
// @homepageURL https://github.com/LucunJi/brainscape-bene
// @supportURL https://github.com/LucunJi/brainscape-bene/issues
// ==/UserScript==
$(document).ready(function (event) {
tryReplaceBtn(event);
});
function tryReplaceBtn(event) {
var timer = setInterval(function () {
var importBtn = $('.import-deck.pro-required');
var exportBtn = $('.export-deck.pro-required');
if (importBtn.length > 0) {
importBtn = importBtn.get(0);
importBtn.replaceWith(importBtn = importBtn.cloneNode(true));
importBtn.classList.remove('pro-required');
importBtn.addEventListener('click', event => {
importCSV(event);
event.stopImmediatePropagation();
});
}
if (exportBtn.length > 0) {
exportBtn = exportBtn.get(0);
exportBtn.replaceWith(exportBtn = exportBtn.cloneNode(true));
exportBtn.classList.remove('pro-required');
exportBtn.addEventListener('click', event => {
exportCSV(event);
event.stopImmediatePropagation();
});
}
}, 50);
event.stopImmediatePropagation();
}
function importCSV(event) {
// get deck title
var packId = $(event.target).parents('.pack-decks-section[data-pack-id]').attr('data-pack-id');
var deckName = prompt('Enter the title of your new deck below');
if (deckName === null) return;
if (deckName.trim() === "") {
alert('The title cannot be an empty string');
return;
}
// get input file
var fileSelector = document.createElement('input');
fileSelector.type = 'file';
fileSelector.accept = '.csv';
fileSelector.onchange = function (event) {
if (event.target.files.length < 1) return;
var reader = new FileReader();
reader.onload = function () {
var parsedCSV = Papa.parse(reader.result);
if (parsedCSV['errors'].length > 0) {
alert(`The csv file is not illegal: ${parsedCSV['errors'][0]['message']}`);
return;
}
// convert to params
var data = new FormData();
var timestamp = Date.now()
for (var entry of parsedCSV['data']) if (entry.length >= 2) {
data.append(`deck[cards_attributes][${timestamp}][reveal]`, 'false');
data.append(`deck[cards_attributes][${timestamp}][question]`, entry[0]);
data.append(`deck[cards_attributes][${timestamp}][answer]`, entry[1]);
timestamp++;
}
// send post request
fetch(`https://www.brainscape.com/api/decks?packID=${packId}`, {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content_type: 'text', name: deckName })
})
.then(res => res.json())
.then(res => fetch(`https://www.brainscape.com/decks/${res['deckID']}/cards/quick_add`, {
method: 'POST',
headers: { 'x-csrf-token': $("[name=csrf-token]").attr('content') },
body: data
})
)
.then(response => location.reload())
.catch(err => alert(`failed to import new deck ${err}`));
};
reader.readAsText(event.target.files[0]);
};
fileSelector.click();
}
function exportCSV(event) {
var packId = $(event.target).parents('.pack-decks-section[data-pack-id]').attr('data-pack-id');
var deckId = $(event.target).parents('.deck-row[id]').attr('id');
var deckName = $(event.target).parents('.deck-name').html();
fetch(`https://www.brainscape.com/api/packs/${packId}/decks/${deckId}/preview`, {
method: 'GET',
})
.then(res => res.json())
.then(res => {
var csvData = res['cards'].map(card =>
[card['question'], card['answer'], card['questionHtml'], card['answerHtml'], card['level']]
);
var fileDownloader = document.createElement('a');
fileDownloader.setAttribute('href',
'data:text/plain;charset=utf-8, ' + encodeURIComponent(Papa.unparse(csvData)));
fileDownloader.setAttribute('download', res['deck']['name'] + '.csv');
fileDownloader.click();
})
}