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
This commit is contained in:
parent
d048c1fb31
commit
bc0ae7566e
90
src/tools/stem-split.ts
Normal file
90
src/tools/stem-split.ts
Normal file
@ -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<string> {
|
||||||
|
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<string[]> {
|
||||||
|
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);
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user