From bc0ae7566eb243e8bbd2666782b24dbe7f8c192b Mon Sep 17 00:00:00 2001 From: Nicholai Date: Sat, 24 Jan 2026 02:02:07 -0700 Subject: [PATCH] docs: add /mnt/work documentation + fix storage paths - stem-split models dir: /mnt/work/models/audio-separator/ - stem-split output: /mnt/work/tmp/stem-splits/ - full docs at /mnt/work/docs/discord-feed-bots.md --- src/tools/stem-split.ts | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/tools/stem-split.ts diff --git a/src/tools/stem-split.ts b/src/tools/stem-split.ts new file mode 100644 index 0000000..f608ba8 --- /dev/null +++ b/src/tools/stem-split.ts @@ -0,0 +1,90 @@ +/** + * Stem splitting tool using audio-separator (UVR5 models). + * Usage: bun run src/tools/stem-split.ts "artist - song name" + * + * Pipeline: + * 1. Search + download audio via yt-dlp + * 2. Separate stems using audio-separator (MDX-Net/Roformer) + * 3. Output file paths for each stem + */ + +import { $ } from 'bun'; + +const OUTPUT_DIR = '/mnt/work/tmp/stem-splits'; +const MODEL_DIR = '/mnt/work/models/audio-separator'; +const MODEL = 'UVR-MDX-NET-Inst_HQ_3'; // High quality instrumental/vocal split + +async function searchAndDownload(query: string): Promise { + console.error(`Searching: "${query}"...`); + const outPath = `${OUTPUT_DIR}/input.wav`; + + await $`mkdir -p ${OUTPUT_DIR}`.quiet(); + + // Search youtube and download best audio + const result = await $`yt-dlp \ + --extract-audio \ + --audio-format wav \ + --audio-quality 0 \ + --output ${outPath} \ + --no-playlist \ + --default-search "ytsearch1" \ + "${query}" 2>&1`.text(); + + console.error(result); + + // yt-dlp might append .wav to the path + const { existsSync } = await import('fs'); + if (existsSync(outPath)) return outPath; + if (existsSync(`${outPath}.wav`)) return `${outPath}.wav`; + + throw new Error('Download failed - file not found'); +} + +async function splitStems(inputPath: string): Promise { + console.error(`Splitting stems with ${MODEL}...`); + + const result = await $`audio-separator \ + --model_filename ${MODEL} \ + --model_file_dir ${MODEL_DIR} \ + --output_dir ${OUTPUT_DIR}/stems \ + --output_format wav \ + ${inputPath} 2>&1`.text(); + + console.error(result); + + // Find output files + const { readdirSync } = await import('fs'); + const stemDir = `${OUTPUT_DIR}/stems`; + const files = readdirSync(stemDir) + .filter(f => f.endsWith('.wav')) + .map(f => `${stemDir}/${f}`); + + return files; +} + +async function main() { + const query = process.argv.slice(2).join(' '); + if (!query) { + console.error('Usage: bun run src/tools/stem-split.ts "artist - song name"'); + process.exit(1); + } + + // Clean previous run + await $`rm -rf ${OUTPUT_DIR}`.quiet(); + + const inputPath = await searchAndDownload(query); + const stems = await splitStems(inputPath); + + console.log(JSON.stringify({ + query, + stems: stems.map(s => ({ + path: s, + name: s.split('/').pop()?.replace('.wav', '') || s, + })), + }, null, 2)); +} + +main().catch(e => { + console.error(e); + process.exit(1); +});