commit b5772e476b9223d56e14e07f195e27438da67e00 Author: Omer Date: Thu Apr 20 20:36:18 2023 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2606ba6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +songs/ +json-data/ +node_modules/ diff --git a/downloadSong.js b/downloadSong.js new file mode 100644 index 0000000..34d947c --- /dev/null +++ b/downloadSong.js @@ -0,0 +1,25 @@ +import https from 'https'; +import fs from 'fs'; + +async function downloadSong(downloadLink, folder, filename, songCount) { + console.log(`Downloading song #${songCount} ...`) + return new Promise((resolve, reject) => { + https.get(downloadLink, (response) => { + response.pipe(fs.createWriteStream(`${folder}/${filename}.mp3`)) + .on('finish', () => { + console.log(`${songCount} songs downloaded successfully`); + resolve(); + }) + .on('error', (error) => { + console.error('Error downloading song:', error); + reject(error); + }); + }); + }); +} + +process.on('message', async (message) => { + const { downloadLink, folder, filename, songCount } = message; + await downloadSong(downloadLink, folder, filename, songCount); + process.send(`Song #${songCount} downloaded successfully`); +}); \ No newline at end of file diff --git a/genres.http b/genres.http new file mode 100644 index 0000000..ebbce77 --- /dev/null +++ b/genres.http @@ -0,0 +1,2 @@ +GET https://api.brain.fm/v2/mental-states/6042a1bad80ae028b821e958/genres/ +Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiJGYmd3dndBT1JCTjlibVJmRUxLcHciLCJleHAiOjE2ODE5ODg5MTUuMzU2LCJpYXQiOjE2ODE5ODgwMTV9.hCs0U5Iv5bgaU9oYi9kmSl0A_gt3V9svL0NiCEvtZFr4DXiNQwkt30fUtwOofMmHUnoEKiRt8ER-K8n5vsXKc23gvvcKaYEzY1kipwlAkK_JiYslE1FXDGXBBN1VrunACWLY5sWyRt1og6WP20BaoD0gIvBr0iazN323mlpU_Ls4yOrid2J_veXAwmCkW_26Q2NbhJeEV2rlwnV95CtxmogYHkoDrDz65UncU5AUvl4ae96il9IN7MSI0DvFl3NutVnD3B2wPaNHNAIoMC1_nLwWl5Q5GMbgC2AW3B1U0pozg78ePFVWq-mKYY8IbgjESNKWg1gRF5oknsFq8shxjw \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..d0e007c --- /dev/null +++ b/main.js @@ -0,0 +1,147 @@ +import fs from 'fs'; +import https from 'https'; +import chalk from 'chalk'; +let songCount = 1; + +const AUTHTOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiJGYmd3dndBT1JCTjlibVJmRUxLcHciLCJleHAiOjE2ODE5ODg5MTUuMzU2LCJpYXQiOjE2ODE5ODgwMTV9.hCs0U5Iv5bgaU9oYi9kmSl0A_gt3V9svL0NiCEvtZFr4DXiNQwkt30fUtwOofMmHUnoEKiRt8ER-K8n5vsXKc23gvvcKaYEzY1kipwlAkK_JiYslE1FXDGXBBN1VrunACWLY5sWyRt1og6WP20BaoD0gIvBr0iazN323mlpU_Ls4yOrid2J_veXAwmCkW_26Q2NbhJeEV2rlwnV95CtxmogYHkoDrDz65UncU5AUvl4ae96il9IN7MSI0DvFl3NutVnD3B2wPaNHNAIoMC1_nLwWl5Q5GMbgC2AW3B1U0pozg78ePFVWq-mKYY8IbgjESNKWg1gRF5oknsFq8shxjw"; + + + +let genres = [ + "Beach", + "Chimes & Bowls", + "Forest", + "Nightsounds", + "Rain", + "Rainforest", + "River", + "Thunder", + "Underwater", + "Wind", + "Acoustic", + "Atmospheric", + "Cinematic", + "Classical", + "Drone", + "Electronic", + "Grooves", + "Lofi", + "Piano", + "Post Rock", +]; + +let mentalStateIDs = { + focus: "6042a1bad80ae028b821e954", + sleep: "6042a1bad80ae028b821e95c", + relax: "6042a1bad80ae028b821e958" +} + +class DownloadQueue { + constructor(maxConcurrency) { + this.queue = []; + this.activeDownloads = 0; + this.maxConcurrency = maxConcurrency; + } + + enqueue(url, folder, filename) { + this.queue.push({url, folder, filename}); + this.processQueue(); + } + + async processQueue() { + if (this.activeDownloads < this.maxConcurrency && this.queue.length > 0) { + const {url, folder, filename} = this.queue.shift(); + this.activeDownloads++; + await downloadSong(url, folder, filename); + console.log(`${songCount} songs downloaded successfully \n${this.queue.length} remaining`) + this.activeDownloads--; + this.processQueue(); + } + } +} + +const downloadQueue = new DownloadQueue(3) + +for(const genre of genres) { + console.log(chalk.red(`Starting genre ${genre}`)) + for(const mentalState of Object.keys(mentalStateIDs)) { + console.log(chalk.yellow(`Starting mental state ${mentalState}`)) + + // + // Phase 1 : Fetch all song data + // + let data = await fetch(`https://api.brain.fm/v2/genres/${genre}/tracks?mentalStateId=${mentalStateIDs[mentalState]}&version=3`, { + headers: { + authorization: `Bearer ${AUTHTOKEN}` + } + }); + data = await data.json(); + + data = formatAudioData(data.result); + + if (checkIfJsonExists(`./json-data/${mentalState}/${genre}.json`)) continue; + ensureDirectory(`./json-data/${mentalState}`); + let file = fs.createWriteStream(`./json-data/${mentalState}/${genre}.json`); + + file.write(JSON.stringify(data)); + + file.close(); + + // + // Phase 2 : Download songs to device + // + + console.log(chalk.green(`Started downloading ${genre} ${mentalState}`)) + for (const song of data) { + let activity = song.serving.track.tags.find(tag => tag.type === "activity").value; + let NEL = song.neuralEffectLevel; + let level = (NEL > 0.66 ? "high" : NEL > 0.33 ? "medium" : "low"); + let hasMultipleNELs = song.hasMultipleNELs; + + let folder = `./songs/${genre}/${mentalState}/${activity}/${hasMultipleNELs ? "mixed" : level}` + let filename = `${song.name.replace(' ', '_')}`; + let downloadLink = `https://audio.brain.fm/${song.url}?userId=FbgwvwAORBN9bmRfELKpw&token=1Uah43y3X33F7ERFwx-_P` + + downloadQueue.enqueue(downloadLink, folder, filename); + } + + } +}; + + + +async function downloadSong(downloadLink, folder, filename) { + return new Promise((resolve, reject) => { + ensureDirectory(folder) + https.get(downloadLink, (response) => { + response.pipe(fs.createWriteStream(`${folder}/${filename}.mp3`)) + .on('finish', () => { + songCount++ + resolve(); + }) + .on('error', (error) => { + console.error('Error downloading song:', error); + reject(error); + }); + }); + }); +} + +function formatAudioData(arr) { + return arr.map(item => { + delete item.serving.track.similarTracks; + return item; + }); +} + +function ensureDirectory(directory) { + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory, { recursive: true }); + } +} + +function checkIfJsonExists(mentalState, genre) { + const filePath = `./json-data/${mentalState}/${genre}.json`; + return fs.existsSync(filePath); +} + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b0e27e0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "brainfm-extract", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "brainfm-extract", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "chalk": "^5.2.0" + } + }, + "node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a246baf --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "brainfm-extract", + "version": "1.0.0", + "description": "", + "main": "main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "chalk": "^5.2.0" + } +}