article-summarizer-jp
Version:
CLI tool for summarizing web articles in Japanese using Anthropic Claude API. Fetches content from URLs and generates both 3-line summaries and full translations in polite Japanese.
109 lines • 5.24 kB
JavaScript
import { program } from 'commander';
import chalk from 'chalk';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { config } from './config.js';
import { fetchContent } from './fetcher.js';
import { summarizeContent } from './summarizer.js';
import { saveToMarkdown } from './markdown.js';
import { getUrlFromUser } from './input.js';
import { startWatchMode } from './watch.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
async function main() {
program
.name('article-summarizer')
.description('日本語記事要約CLIツール')
.version(packageJson.version)
.argument('[urls...]', 'summarize articles from the provided URLs (supports multiple URLs)')
.option('--config', 'configure API key')
.option('-w, --watch', 'start in watch mode for continuous URL input')
.option('-d, --date-prefix', 'add date prefix to filename (YYYY-MM-DD_title.md format)')
.option('-s, --simplify', 'output only 3-line summary without details')
.parse();
const options = program.opts();
const args = program.args;
try {
if (options.config) {
await config.configure();
console.log(chalk.green('✓ 設定が完了しました'));
return;
}
if (options.watch) {
await startWatchMode(options.datePrefix, options.simplify);
return;
}
// Get URLs from arguments or prompt user
let urls = [];
if (args.length > 0) {
urls = args;
}
else {
const url = await getUrlFromUser();
urls = [url];
}
if (!config.hasApiKey()) {
console.log(chalk.yellow('APIキーが設定されていません。最初に設定を行ってください。'));
await config.configure();
}
console.log(chalk.blue(`📄 ${urls.length}件の記事を処理開始します(最大5件並行処理)...\n`));
const results = [];
const maxConcurrent = 5;
// Process URLs in batches with concurrent execution
for (let i = 0; i < urls.length; i += maxConcurrent) {
const batch = urls.slice(i, i + maxConcurrent);
const batchPromises = batch.map(async (url, index) => {
const globalIndex = i + index + 1;
const total = urls.length;
try {
console.log(chalk.blue(`[${globalIndex}/${total}] ${url}`));
console.log(chalk.gray(' 📄 コンテンツを取得中...'));
const { title, extractedUrl, htmlContent } = await fetchContent(url);
console.log(chalk.gray(' 🤖 記事を要約・翻訳中...'));
const { summary, details, translatedTitle, tags, validImageUrl } = await summarizeContent(title, htmlContent, extractedUrl, false, options.simplify);
console.log(chalk.gray(' 💾 マークダウンファイルに保存中...'));
const filename = await saveToMarkdown(translatedTitle, extractedUrl, summary, details, tags, validImageUrl, options.datePrefix, options.simplify);
console.log(chalk.green(` ✅ 完了: ${filename}\n`));
return { success: true, filename, url };
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.log(chalk.red(` ❌ エラー: ${errorMessage}\n`));
return { success: false, url, error: errorMessage };
}
});
// Wait for all promises in the current batch to complete
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
// Show summary
console.log(chalk.bold('\n📊 処理結果:'));
console.log(chalk.gray('='.repeat(50)));
const successful = results.filter((r) => r.success);
const failed = results.filter((r) => !r.success);
console.log(chalk.green(`✅ 成功: ${successful.length}件`));
if (successful.length > 0) {
successful.forEach((result) => {
console.log(chalk.gray(` 📄 ${result.filename}`));
});
}
if (failed.length > 0) {
console.log(chalk.red(`\n❌ 失敗: ${failed.length}件`));
failed.forEach((result) => {
console.log(chalk.gray(` 🔗 ${result.url}`));
console.log(chalk.gray(` 💥 ${result.error}`));
});
}
console.log(chalk.gray('='.repeat(50)));
console.log(chalk.bold(`🎯 合計: ${results.length}件中 ${successful.length}件成功\n`));
}
catch (error) {
console.error(chalk.red('エラー:', error instanceof Error ? error.message : 'Unknown error'));
process.exit(1);
}
}
main();
//# sourceMappingURL=index.js.map