Updated to v3 api
Senseless JSDoc
This commit is contained in:
261
main.js
261
main.js
@@ -1,66 +1,229 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import load from 'dotenv';
|
import {config} from 'dotenv';
|
||||||
load()
|
config()
|
||||||
|
|
||||||
let songCount = 1;
|
let songCount = 1;
|
||||||
|
|
||||||
const AUTHTOKEN = process.env.AUTHTOKEN;
|
const AUTHTOKEN = process.env.AUTHTOKEN;
|
||||||
const TOKEN = process.env.TOKEN;
|
|
||||||
const USERID = process.env.USERID;
|
|
||||||
|
|
||||||
if(!AUTHTOKEN || !TOKEN || !USERID) {
|
if (!AUTHTOKEN) {
|
||||||
console.error('Please enter all the required information');
|
console.error('Please enter all the required information');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @typedef {("focus" | "relax" | "sleep" | "meditate")} mentalState */
|
||||||
|
|
||||||
|
/** @type {mentalState[]} */
|
||||||
|
let mentalStates = ["focus", "relax", "sleep", "meditate"]
|
||||||
|
|
||||||
let genres = [
|
/**
|
||||||
"Beach",
|
* @type {Record<mentalState, {base: string[], nature: string[]}>}
|
||||||
"Chimes & Bowls",
|
*/
|
||||||
"Forest",
|
let genres = {
|
||||||
"Nightsounds",
|
"focus": {
|
||||||
"Rain",
|
base: [
|
||||||
"Rainforest",
|
"Acoustic",
|
||||||
"River",
|
"Atmospheric",
|
||||||
"Thunder",
|
"Cinematic",
|
||||||
"Underwater",
|
"Classical",
|
||||||
"Wind",
|
"Drone",
|
||||||
"Acoustic",
|
"Electronic",
|
||||||
"Atmospheric",
|
"Grooves",
|
||||||
"Cinematic",
|
"Lofi",
|
||||||
"Classical",
|
"Piano",
|
||||||
"Drone",
|
"Post Rock"
|
||||||
"Electronic",
|
],
|
||||||
"Grooves",
|
nature: [
|
||||||
"Lofi",
|
"Beach",
|
||||||
"Piano",
|
"Chimes & Bowls",
|
||||||
"Post Rock",
|
"Forest",
|
||||||
];
|
"Nightsounds",
|
||||||
|
"Rain",
|
||||||
|
"Rainforest",
|
||||||
|
"River",
|
||||||
|
"Thunder",
|
||||||
|
"Underwater",
|
||||||
|
"Wind"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"relax": {
|
||||||
|
base: [
|
||||||
|
"Atmospheric",
|
||||||
|
"Electronic"
|
||||||
|
],
|
||||||
|
nature: [
|
||||||
|
"Beach",
|
||||||
|
"Chimes & Bowls",
|
||||||
|
"Forest",
|
||||||
|
"Nightsounds",
|
||||||
|
"Rain",
|
||||||
|
"Rainforest",
|
||||||
|
"River",
|
||||||
|
"Thunder",
|
||||||
|
"Underwater",
|
||||||
|
"Wind"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sleep": {
|
||||||
|
base: [
|
||||||
|
"Atmospheric"
|
||||||
|
],
|
||||||
|
nature: [
|
||||||
|
"Beach",
|
||||||
|
"Forest",
|
||||||
|
"Nightsounds",
|
||||||
|
"Rain",
|
||||||
|
"Rainforest",
|
||||||
|
"River",
|
||||||
|
"Thunder",
|
||||||
|
"Underwater",
|
||||||
|
"Wind"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"meditate": {
|
||||||
|
base: [
|
||||||
|
"Atmospheric",
|
||||||
|
"Electronic"
|
||||||
|
],
|
||||||
|
nature: [
|
||||||
|
"Beach",
|
||||||
|
"Chimes & Bowls",
|
||||||
|
"Forest",
|
||||||
|
"Nightsounds",
|
||||||
|
"Rain",
|
||||||
|
"Rainforest",
|
||||||
|
"River",
|
||||||
|
"Thunder",
|
||||||
|
"Underwater",
|
||||||
|
"Wind"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mentalStateIDs = {
|
// @ts-ignore
|
||||||
focus: "6042a1bad80ae028b821e954",
|
let moods = {
|
||||||
sleep: "6042a1bad80ae028b821e95c",
|
"focus": [
|
||||||
relax: "6042a1bad80ae028b821e958"
|
"Brooding",
|
||||||
|
"Calm",
|
||||||
|
"Chill",
|
||||||
|
"Dark",
|
||||||
|
"Downtempo",
|
||||||
|
"Dreamlike",
|
||||||
|
"Driving",
|
||||||
|
"Energizing",
|
||||||
|
"Epic",
|
||||||
|
"Floating",
|
||||||
|
"Heavy",
|
||||||
|
"Hopeful",
|
||||||
|
"Inspiring",
|
||||||
|
"Meditative",
|
||||||
|
"Mysterious",
|
||||||
|
"Ominous",
|
||||||
|
"Optimistic",
|
||||||
|
"Playful",
|
||||||
|
"Ponderous",
|
||||||
|
"Serene",
|
||||||
|
"Strong",
|
||||||
|
"Upbeat",
|
||||||
|
"Uplifting"
|
||||||
|
],
|
||||||
|
"relax": [
|
||||||
|
"Brooding",
|
||||||
|
"Calm",
|
||||||
|
"Chill",
|
||||||
|
"Dark",
|
||||||
|
"Downtempo",
|
||||||
|
"Dreamlike",
|
||||||
|
"Driving",
|
||||||
|
"Energizing",
|
||||||
|
"Epic",
|
||||||
|
"Floating",
|
||||||
|
"Hopeful",
|
||||||
|
"Inspiring",
|
||||||
|
"Meditative",
|
||||||
|
"Mysterious",
|
||||||
|
"Optimistic",
|
||||||
|
"Playful",
|
||||||
|
"Ponderous",
|
||||||
|
"Serene",
|
||||||
|
"Strong",
|
||||||
|
"Upbeat",
|
||||||
|
"Uplifting"
|
||||||
|
],
|
||||||
|
"sleep": [
|
||||||
|
"Brooding",
|
||||||
|
"Calm",
|
||||||
|
"Chill",
|
||||||
|
"Dark",
|
||||||
|
"Dreamlike",
|
||||||
|
"Epic",
|
||||||
|
"Floating",
|
||||||
|
"Heavy",
|
||||||
|
"Meditative",
|
||||||
|
"Mysterious",
|
||||||
|
"Optimistic",
|
||||||
|
"Ponderous",
|
||||||
|
"Serene",
|
||||||
|
"Strong"
|
||||||
|
],
|
||||||
|
"meditate": [
|
||||||
|
"Brooding",
|
||||||
|
"Calm",
|
||||||
|
"Chill",
|
||||||
|
"Dark",
|
||||||
|
"Downtempo",
|
||||||
|
"Dreamlike",
|
||||||
|
"Driving",
|
||||||
|
"Energizing",
|
||||||
|
"Epic",
|
||||||
|
"Floating",
|
||||||
|
"Heavy",
|
||||||
|
"Hopeful",
|
||||||
|
"Inspiring",
|
||||||
|
"Meditative",
|
||||||
|
"Mysterious",
|
||||||
|
"Optimistic",
|
||||||
|
"Playful",
|
||||||
|
"Ponderous",
|
||||||
|
"Serene",
|
||||||
|
"Strong",
|
||||||
|
"Upbeat",
|
||||||
|
"Uplifting"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{url: string, folder: string, filename: string}} QueueEntry
|
||||||
|
*/
|
||||||
|
|
||||||
class DownloadQueue {
|
class DownloadQueue {
|
||||||
|
/** @param {number} maxConcurrency */
|
||||||
constructor(maxConcurrency) {
|
constructor(maxConcurrency) {
|
||||||
|
/** @type {QueueEntry[]} */
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
this.activeDownloads = 0;
|
this.activeDownloads = 0;
|
||||||
this.maxConcurrency = maxConcurrency;
|
this.maxConcurrency = maxConcurrency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string} folder
|
||||||
|
* @param {string} filename
|
||||||
|
*/
|
||||||
enqueue(url, folder, filename) {
|
enqueue(url, folder, filename) {
|
||||||
this.queue.push({url, folder, filename});
|
this.queue.push({ url, folder, filename });
|
||||||
this.processQueue();
|
this.processQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
async processQueue() {
|
async processQueue() {
|
||||||
if (this.activeDownloads < this.maxConcurrency && this.queue.length > 0) {
|
if (this.activeDownloads < this.maxConcurrency && this.queue.length > 0) {
|
||||||
const {url, folder, filename} = this.queue.shift();
|
// @ts-ignore
|
||||||
|
const { url, folder, filename } = this.queue.shift();
|
||||||
this.activeDownloads++;
|
this.activeDownloads++;
|
||||||
await downloadSong(url, folder, filename);
|
await downloadSong(url, folder, filename);
|
||||||
console.log(`${songCount} songs downloaded successfully \n${this.queue.length} remaining`)
|
console.log(`${songCount} songs downloaded successfully \n${this.queue.length} remaining`)
|
||||||
@@ -72,21 +235,22 @@ class DownloadQueue {
|
|||||||
|
|
||||||
const downloadQueue = new DownloadQueue(3)
|
const downloadQueue = new DownloadQueue(3)
|
||||||
|
|
||||||
for(const genre of genres) {
|
for (const mentalState of mentalStates) {
|
||||||
console.log(chalk.red(`Starting genre ${genre}`))
|
console.log(chalk.red(`Starting mental state ${mentalState}`))
|
||||||
for(const mentalState of Object.keys(mentalStateIDs)) {
|
for (const genre of [...genres[mentalState].base, ...genres[mentalState].nature]) {
|
||||||
console.log(chalk.yellow(`Starting mental state ${mentalState}`))
|
console.log(chalk.yellow(`Starting genre ${genre}`))
|
||||||
|
|
||||||
//
|
//
|
||||||
// Phase 1 : Fetch all song data
|
// Phase 1 : Fetch all song data
|
||||||
//
|
//
|
||||||
let data = await fetch(`https://api.brain.fm/v2/genres/${genre}/tracks?mentalStateId=${mentalStateIDs[mentalState]}&version=3`, {
|
let data = await fetch(`https://api.brain.fm/v3/servings/search?genre=${genre}&dynamicMentalStateId=${mentalState}`, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${AUTHTOKEN}`
|
authorization: `Bearer ${AUTHTOKEN}`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
data = await data.json();
|
data = await data.json();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
data = formatAudioData(data.result);
|
data = formatAudioData(data.result);
|
||||||
|
|
||||||
if (checkIfJsonExists(`./json-data/${mentalState}/${genre}.json`)) continue;
|
if (checkIfJsonExists(`./json-data/${mentalState}/${genre}.json`)) continue;
|
||||||
@@ -102,15 +266,16 @@ for(const genre of genres) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
console.log(chalk.green(`Started downloading ${genre} ${mentalState}`))
|
console.log(chalk.green(`Started downloading ${genre} ${mentalState}`))
|
||||||
|
// @ts-ignore
|
||||||
for (const song of data) {
|
for (const song of data) {
|
||||||
let activity = song.serving.track.tags.find(tag => tag.type === "activity").value;
|
// @ts-ignore
|
||||||
let NEL = song.neuralEffectLevel;
|
let activity = song.track.tags.filter(x => x.type == 'activity').map(x => x.value).join('/');
|
||||||
|
let NEL = song.trackVariation.neuralEffectLevel;
|
||||||
let level = (NEL > 0.66 ? "high" : NEL > 0.33 ? "medium" : "low");
|
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 folder = `./songs/${genre}/${mentalState}/${activity}/${level}`
|
||||||
let filename = `${song.name.replace(' ', '_')}`;
|
let filename = song.trackVariation.baseUrl;
|
||||||
let downloadLink = `https://audio.brain.fm/${song.url}?userId=${USERID}&token=${TOKEN}`;
|
let downloadLink = song.trackVariation.tokenedUrl;
|
||||||
|
|
||||||
downloadQueue.enqueue(downloadLink, folder, filename);
|
downloadQueue.enqueue(downloadLink, folder, filename);
|
||||||
}
|
}
|
||||||
@@ -120,6 +285,7 @@ for(const genre of genres) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
async function downloadSong(downloadLink, folder, filename) {
|
async function downloadSong(downloadLink, folder, filename) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ensureDirectory(folder)
|
ensureDirectory(folder)
|
||||||
@@ -127,6 +293,7 @@ async function downloadSong(downloadLink, folder, filename) {
|
|||||||
response.pipe(fs.createWriteStream(`${folder}/${filename}.mp3`))
|
response.pipe(fs.createWriteStream(`${folder}/${filename}.mp3`))
|
||||||
.on('finish', () => {
|
.on('finish', () => {
|
||||||
songCount++
|
songCount++
|
||||||
|
// @ts-ignore
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.on('error', (error) => {
|
.on('error', (error) => {
|
||||||
@@ -137,19 +304,23 @@ async function downloadSong(downloadLink, folder, filename) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
function formatAudioData(arr) {
|
function formatAudioData(arr) {
|
||||||
|
// @ts-ignore
|
||||||
return arr.map(item => {
|
return arr.map(item => {
|
||||||
delete item.serving.track.similarTracks;
|
delete item.track.similarTracks;
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
function ensureDirectory(directory) {
|
function ensureDirectory(directory) {
|
||||||
if (!fs.existsSync(directory)) {
|
if (!fs.existsSync(directory)) {
|
||||||
fs.mkdirSync(directory, { recursive: true });
|
fs.mkdirSync(directory, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
function checkIfJsonExists(mentalState, genre) {
|
function checkIfJsonExists(mentalState, genre) {
|
||||||
const filePath = `./json-data/${mentalState}/${genre}.json`;
|
const filePath = `./json-data/${mentalState}/${genre}.json`;
|
||||||
return fs.existsSync(filePath);
|
return fs.existsSync(filePath);
|
||||||
|
|||||||
Reference in New Issue
Block a user