Created
April 18, 2021 18:26
-
-
Save danikaze/9945110998d3e88380325c59ba973eb0 to your computer and use it in GitHub Desktop.
Download, organize and keep sync with tabletopaudio.com files with this gist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * https://tabletopaudio.com/ is an awesome source of free music. | |
| * Consider making a donation (to him) if using his creations! | |
| * | |
| * | |
| * Just place the following .js+.bat files in the location you want to download | |
| * from tabletop audio ^^ | |
| * | |
| * Creating symboling links (to put files inside folders based on categories) | |
| * requires admin permissions in Win :( | |
| * But it can be requested if the JS is run from the following `.bat` file: | |
| @echo off | |
| :: BatchGotAdmin | |
| :------------------------------------- | |
| REM --> Check for permissions | |
| IF "%PROCESSOR_ARCHITECTURE%" EQU "amd64" ( | |
| >nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system" | |
| ) ELSE ( | |
| >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" | |
| ) | |
| REM --> If error flag set, we do not have admin. | |
| if '%errorlevel%' NEQ '0' ( | |
| echo Requesting administrative privileges... | |
| goto UACPrompt | |
| ) else ( goto gotAdmin ) | |
| :UACPrompt | |
| echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" | |
| set params= %* | |
| echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs" | |
| "%temp%\getadmin.vbs" | |
| del "%temp%\getadmin.vbs" | |
| exit /B | |
| :gotAdmin | |
| pushd "%CD%" | |
| CD /D "%~dp0" | |
| :-------------------------------------- | |
| node tabletop-audio-dl.js | |
| pause | |
| */ | |
| /* | |
| * .js file starts here | |
| */ | |
| const { get } = require('https'); | |
| const { join } = require('path'); | |
| const { existsSync, createWriteStream, mkdirSync } = require('fs'); | |
| const util = require('util'); | |
| const exec = util.promisify(require('child_process').exec); | |
| const URL = 'https://tabletopaudio.com'; | |
| const SAVE_FOLDER = process.cwd(); | |
| const DELAY = 3000; | |
| function getPage() { | |
| return new Promise((resolve, reject) => { | |
| get(URL, (res) => { | |
| let html = ''; | |
| res.on('data', data => { | |
| html += data; | |
| }); | |
| res.on('end', () => resolve(html)); | |
| }); | |
| }) | |
| } | |
| function getCategories(html) { | |
| const cat = []; | |
| const re = /data-filter="\.([^"]+)"/g; | |
| let match = re.exec(html); | |
| while(match) { | |
| cat.push(match[1]); | |
| match = re.exec(html); | |
| } | |
| return cat; | |
| } | |
| function getSubstring(text, start, end, offset = 0) { | |
| const i = text.indexOf(start, offset); | |
| if (i === -1) return; | |
| const j = text.indexOf(end, i + start.length); | |
| if (j === -1) return; | |
| return { | |
| text: text.substring(i + start.length, j), | |
| next: j + end.length | |
| }; | |
| } | |
| function getSongs(html, categoryList) { | |
| const files = []; | |
| let foundCategories = getSubstring(html, 'class="col-md-3 mix', '"'); | |
| while (foundCategories) { | |
| const categories = foundCategories.text.split(' ').filter(c => categoryList.indexOf(c) !== -1); | |
| const foundFilename = getSubstring(html, `onclick="saveAs('`, "'", foundCategories.next); | |
| if (!foundFilename) { | |
| foundCategories = getSubstring(html, 'class="col-md-3 mix', '"', foundCategories.next); | |
| continue; | |
| } | |
| const filename = foundFilename.text + '.mp3'; | |
| files.push({ filename, categories }); | |
| foundCategories = getSubstring(html, 'class="col-md-3 mix', '"', foundFilename.next); | |
| } | |
| return files; | |
| } | |
| function filterSongs(songs) { | |
| return songs.filter(song => { | |
| const filePath = join (SAVE_FOLDER, song.filename); | |
| if (existsSync(filePath)) { | |
| createLinks(song); | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| async function createLinks(song) { | |
| for (let i = 0; i < song.categories.length; i++) { | |
| const cat = song.categories[i]; | |
| const src = join(SAVE_FOLDER, song.filename); | |
| const folder = join(SAVE_FOLDER, cat); | |
| const target = join(folder, song.filename); | |
| if (existsSync(target)) { | |
| return; | |
| } | |
| if (!existsSync(folder)) { | |
| mkdirSync(folder); | |
| } | |
| console.log(` > Creating link for [${cat}] ${song.filename}`); | |
| await createLink(src, target); | |
| } | |
| } | |
| async function createLink(source, target) { | |
| try { | |
| await exec(`mklink "${target}" "${source}"`); | |
| } catch (e) { | |
| console.warn(e); | |
| } | |
| } | |
| function downloadSong(song) { | |
| return new Promise((resolve, reject) => { | |
| const targetPath = join(SAVE_FOLDER, song.filename); | |
| const file = createWriteStream(targetPath); | |
| const url = `https://sounds.tabletopaudio.com/${song.filename}`; | |
| get(url, (res) => { | |
| res.on('end', () => { | |
| resolve() | |
| }); | |
| res.on('close', () => resolve()); | |
| res.pipe(file) | |
| }); | |
| }) | |
| } | |
| function getSongsPerCategory(songs, prefix) { | |
| const cats = songs.reduce((acc, song) => { | |
| song.categories.forEach(cat => { | |
| acc[cat] = (acc[cat] || 0) + 1; | |
| }) | |
| return acc; | |
| }, {}); | |
| return Object.keys(cats).sort().map((cat) => `${prefix}${cat} (${cats[cat]})`).join('\n'); | |
| } | |
| function delay(ms) { | |
| return new Promise((resolve) => { | |
| setTimeout(resolve, ms); | |
| }); | |
| } | |
| async function run() { | |
| console.log(` * Target folder: ${SAVE_FOLDER}`); | |
| console.log(` * Requesting html: ${URL}`); | |
| const html = await getPage(); | |
| const categories = getCategories(html); | |
| const songs = getSongs(html, categories); | |
| const newSongs = filterSongs(songs); | |
| console.log(` * Detected a total of ${songs.length} songs`); | |
| console.log(getSongsPerCategory(songs, ' - ')); | |
| console.log(` * Downloading ${newSongs.length} new files:`); | |
| for (let i = 0; i < newSongs.length; i++) { | |
| const song = newSongs[i]; | |
| console.log(` - Downloading [${i + 1}/${newSongs.length}] ${song.filename}`); | |
| try { | |
| await downloadSong(song); | |
| createLinks(song); | |
| await delay(DELAY); | |
| } catch (e) { | |
| console.warn(` (!) Error downloading ${song.filename}`, e); | |
| } | |
| } | |
| } | |
| run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment