diff --git a/index.ts b/index.ts index 41f7431..45cdd57 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,5 @@ #!/usr/bin/env node -import { parseArgs } from "node:util"; +import util from "node:util"; import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { streamText } from "ai"; @@ -7,7 +7,7 @@ import { initConfig, loadConfig } from "./src/config"; import { Conversation } from "./src/conversation"; import Renderer from "./src/render"; -const { values: args, positionals } = parseArgs({ +const { values: args, positionals } = util.parseArgs({ args: process.argv.slice(2), options: { model: { type: "string" }, diff --git a/tts.ts b/tts.ts index 844166b..7bf3ceb 100644 --- a/tts.ts +++ b/tts.ts @@ -1,10 +1,11 @@ #!/usr/bin/env node +import events from "node:events"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import process from "node:process"; -import { createInterface } from "node:readline"; -import { parseArgs } from "node:util"; +import readline from "node:readline"; +import util from "node:util"; import { Readability } from "@mozilla/readability"; import chalk from "chalk"; import { execa } from "execa"; @@ -14,7 +15,7 @@ import wrapAnsi from "wrap-ansi"; async function main() { const start = Date.now(); - const { values: args } = parseArgs({ + const { values: args } = util.parseArgs({ args: process.argv.slice(2), options: { wait: { type: "boolean" }, @@ -32,41 +33,34 @@ async function main() { const url = args.url ?? args.u; const edit = args.edit ?? args.e; - let input = url - ? url - : await new Promise((resolve) => { - let data = ""; - process.stdin.setEncoding("utf8"); - process.stdin.on("data", (chunk) => (data += chunk)); - process.stdin.on("end", () => resolve(data)); - }); - input = input.trim(); + let input = ""; - if (url !== undefined) { - const result = await fetch(input); - const window = new Window({ url: input }); + if (url?.trim()) { + const result = await fetch(url.trim()); + const window = new Window({ url: url.trim() }); const doc = window.document; doc.write(await result.text()); await window.happyDOM.waitUntilComplete(); const readable = new Readability(doc).parse(); - if (readable?.textContent) input = readable.textContent.trim(); - if (input === result.url) { - console.error("Unable to read web page."); - process.exit(1); + if (!readable?.textContent) { + throw "Unable to read web page."; } + input = readable.textContent.trim(); + } else { + process.stdin.setEncoding("utf8"); + process.stdin.on("data", (data) => { + input += data; + }); + await events.once(process.stdin, "end"); } if (!input) { - console.error("No input given."); - process.exit(1); + throw "No input given."; } const home = os.homedir(); const cwd = path.join(home, ".ttsc"); - const wavs = fs.glob(path.join(cwd, "*.wav")); - for await (const wav of wavs) await fs.rm(wav); - if (edit) { const file = path.join(cwd, "input.txt"); await fs.writeFile(file, input); @@ -75,7 +69,8 @@ async function main() { cwd, stdin: "inherit", stdout: "inherit", - })`${editor} input.txt`; + stderr: "inherit", + })`${editor} ${file}`; input = await fs.readFile(file, "utf8"); } @@ -86,6 +81,9 @@ async function main() { ), ); + const wavs = fs.glob(path.join(cwd, "*.wav")); + for await (const wav of wavs) await fs.rm(wav); + const koko = execa({ cwd, input, @@ -103,7 +101,8 @@ async function main() { } let first = true; - const rl = createInterface({ input: koko.stdout }); + const files = []; + const rl = readline.createInterface({ input: koko.stdout }); for await (const line of rl) { const trimmed = line.trim(); @@ -124,13 +123,14 @@ async function main() { console.log(chalk.green(`\nTime-to-audio: ${duration}s`)); } - const [columns] = process.stdout.getWindowSize(); - const text = wrapAnsi(info.text, columns); const file = path.join(cwd, `${info.file}.wav`); if (wait) { + files.push(file); process.stdout.write("."); } else { + const [columns] = process.stdout.getWindowSize(); + const text = wrapAnsi(info.text, columns); console.log(`\n${chalk.blue(file)}\n${text}\n`); await execa({ cwd, @@ -142,7 +142,7 @@ async function main() { } await koko; - if (!wait) process.exit(); + if (!wait) return; const stop = Date.now(); const duration = ((stop - start) / 1000).toFixed(2); @@ -150,8 +150,7 @@ async function main() { console.log(chalk.yellow(`Combining audio with sox...\n`)); try { - const wavs = await Array.fromAsync(fs.glob(path.join(cwd, "*.wav"))); - await execa({ cwd, shell: true })`sox ${wavs} output.wav`; + await execa({ cwd, shell: true })`sox ${files} output.wav`; } catch (err) { if (err instanceof Object && "code" in err && err.code === "ENOENT") { console.error("Sox not found. Unable to combine outputs."); @@ -159,7 +158,7 @@ async function main() { throw err; } console.log(chalk.yellow(`Audio available in ${cwd}`)); - process.exit(); + return; } const [columns] = process.stdout.getWindowSize();