diff --git a/index.ts b/index.ts index 490ec32..85da1fe 100644 --- a/index.ts +++ b/index.ts @@ -1,9 +1,10 @@ import { Hono } from 'hono' const app = new Hono() const joypixels = require("emoji-toolkit"); -import { mkdirSync, writeFileSync, existsSync, rmSync, readFileSync, readFile } from 'fs' +import { mkdirSync, writeFileSync, existsSync, rmSync, readFileSync, readFile, writeFile } from 'fs' import { $ } from 'bun'; import ytSearch from 'yt-search'; +import { writeMDFPWMv3 } from './mdfpwmWriter'; let ytdlpPath = "yt-dlp" if(existsSync("./yt-dlp")) { ytdlpPath = "./yt-dlp" @@ -22,22 +23,49 @@ app.post("/youtube", async (c) => { let title = body.title; let artist = body.artist; + let filename; + if(body.mdfpwm) { + filename = "cache/"+Bun.hash(title+"-"+artist)+".mdfpwm" - let filename = "cache/"+Bun.hash(title+"-"+artist)+".dfpwm" + if(!existsSync(filename)) { + const searchResult = await ytSearch(`${artist} ${title}`); + const video = searchResult.videos[0]; + if (!video) throw new Error("No video found"); + console.log(video) - if(!existsSync(filename)) { - const searchResult = await ytSearch(`${artist} ${title}`); - const video = searchResult.videos[0]; - if (!video) throw new Error("No video found"); - console.log(video) - await $`${ytdlpPath} --audio-format opus -x ${video.url} -o test`; - await $`ffmpeg -i test.opus -af "highpass=f=60, lowpass=f=15000, dynaudnorm" -ar 48000 -ac 1 -f dfpwm "${filename}"`; - rmSync("test.opus") + await $`${ytdlpPath} --audio-format opus -x ${video.url} -o test`; + console.timeEnd("yt-dlp") + + // this makes left.dfpwm & right.dfpwm + await $`ffmpeg -i test.opus -filter_complex "[0:a]channelsplit=channel_layout=stereo[left][right]; [left]highpass=f=60,lowpass=f=15000,dynaudnorm[leftf]; [right]highpass=f=60,lowpass=f=15000,dynaudnorm[rightf]" -map "[leftf]" -ar 48000 -f dfpwm left.dfpwm -map "[rightf]" -ar 48000 -f dfpwm right.dfpwm` + rmSync("test.opus") + + writeFileSync(filename, writeMDFPWMv3( + readFileSync("left.dfpwm"), + readFileSync("right.dfpwm"), + { title: title, artist: artist, album: title } + )) + + rmSync("right.dfpwm") + rmSync("left.dfpwm") + } + } else { + filename = "cache/"+Bun.hash(title+"-"+artist)+".dfpwm" + + if(!existsSync(filename)) { + const searchResult = await ytSearch(`${artist} ${title}`); + const video = searchResult.videos[0]; + if (!video) throw new Error("No video found"); + console.log(video) + await $`${ytdlpPath} --audio-format opus -x ${video.url} -o test`; + await $`ffmpeg -i test.opus -af "highpass=f=60, lowpass=f=15000, dynaudnorm" -ar 48000 -ac 1 -f dfpwm "${filename}"`; + rmSync("test.opus") + } } - + return c.body(readFileSync(filename), { headers: { - 'Content-Type': 'audio/vnd.dfpwm', + 'Content-Type': `audio/vnd.${body.mdfpwm ? "m" : ""}dfpwm`, 'Content-Disposition': `attachment; filename="${filename}"`, }, }); diff --git a/mdfpwmWriter.ts b/mdfpwmWriter.ts new file mode 100644 index 0000000..a01c6a4 --- /dev/null +++ b/mdfpwmWriter.ts @@ -0,0 +1,49 @@ +export interface MDFPWMMetadata { + title?: string; + artist?: string; + album?: string; +} + +function toS1(str: string): Buffer { + const utf8 = Buffer.from(str, "utf-8"); + const len = utf8.length; + return Buffer.concat([Buffer.from([len]), utf8]); +} + +export function writeMDFPWMv3( + left: Buffer, + right: Buffer, + meta: MDFPWMMetadata = {} +): Buffer { + if (left.length !== right.length) { + throw new Error("Left and right DFPWM buffers must be the same length"); + } + + let length = left.byteLength*2; + const mdfpwm = [] + mdfpwm.push(Buffer.from("MDFPWM\x03", "ascii")); + mdfpwm.push(Buffer.alloc(4)); + mdfpwm.push(toS1(meta.artist || "")); + mdfpwm.push(toS1(meta.title || "")); + mdfpwm.push(toS1(meta.album || "")); + mdfpwm[1]!.writeUint32LE(length) + + for(let i =0; i < length / 12000; i++) { + const lStart = i * 6000; + const rStart = i * 6000; + + const lChunk = left.subarray(lStart, lStart + 6000); + mdfpwm.push(lChunk); + if (lChunk.length < 6000) { + mdfpwm.push(Buffer.alloc(6000 - lChunk.length, 0x55)); + } + + const rChunk = right.subarray(rStart, rStart + 6000); + mdfpwm.push(rChunk); + if (rChunk.length < 6000) { + mdfpwm.push(Buffer.alloc(6000 - rChunk.length, 0x55)); + } + } + + return Buffer.concat(mdfpwm) +}