UNPKG

@surgbc/egw-writings-downloader

Version:

Data extraction and database population tool for EGW Writings

240 lines (239 loc) • 9.71 kB
#!/usr/bin/env node import 'dotenv/config'; import { Command } from 'commander'; import { createAuthManager, createApiClient, EGWDatabase } from '@surgbc/egw-writings-shared'; const program = new Command(); program .name('egw-downloader') .description('Download and index EGW Writings content') .version('1.0.0'); program .command('languages') .description('Download and index all available languages') .action(async () => { console.log('šŸ“š Downloading languages...'); const authManager = createAuthManager(); const apiClient = createApiClient(authManager); const db = new EGWDatabase(); try { const languages = await apiClient.getLanguages(); console.log(`Found ${languages.length} languages`); for (const lang of languages) { db.insertLanguage(lang.code, lang.name, lang.direction); console.log(`āœ… ${lang.name} (${lang.code})`); } console.log(`šŸŽ‰ Successfully indexed ${languages.length} languages`); } catch (error) { console.error('āŒ Error downloading languages:', error); process.exit(1); } finally { db.close(); } }); program .command('books') .description('Download book metadata for a language') .option('-l, --lang <language>', 'Language code', 'en') .option('--limit <number>', 'Limit number of books', '100') .action(async (options) => { console.log(`šŸ“– Downloading books for language: ${options.lang}`); const authManager = createAuthManager(); const apiClient = createApiClient(authManager); const db = new EGWDatabase(); try { // Get folders for the language const folders = await apiClient.getFolders(options.lang); console.log(`Found ${folders.length} folders`); let totalBooks = 0; // Process all folders and their children for (const folder of folders) { if (folder.children) { for (const subfolder of folder.children) { if (subfolder.nbooks > 0) { console.log(`šŸ“ Processing folder: ${subfolder.name} (${subfolder.nbooks} books)`); const books = await apiClient.getBooksByFolder(subfolder.folder_id); const limit = parseInt(options.limit); const booksToProcess = books.slice(0, limit); for (const book of booksToProcess) { db.insertBook(book); totalBooks++; console.log(` āœ… ${book.title} by ${book.author}`); } } } } } console.log(`šŸŽ‰ Successfully indexed ${totalBooks} books`); } catch (error) { console.error('āŒ Error downloading books:', error); process.exit(1); } finally { db.close(); } }); program .command('content') .description('Download full content for books') .option('-l, --lang <language>', 'Language code', 'en') .option('-b, --book <bookId>', 'Specific book ID to download') .option('--limit <number>', 'Limit number of books', '10') .action(async (options) => { console.log('šŸ“„ Downloading book content...'); const authManager = createAuthManager(); const apiClient = createApiClient(authManager); const db = new EGWDatabase(); try { let books; if (options.book) { // Download specific book const book = db.getBook(parseInt(options.book)); if (!book) { throw new Error(`Book ${options.book} not found in database`); } books = [book]; } else { // Download books for language books = db.getBooks(options.lang).slice(0, parseInt(options.limit)); } console.log(`Processing ${books.length} books...`); for (const book of books) { const typedBook = book; console.log(`\nšŸ“– Downloading content for: ${typedBook.title}`); try { // Get table of contents const toc = await apiClient.getBookToc(typedBook.book_id); console.log(` Found ${toc.length} chapters`); let totalParagraphs = 0; // Download each chapter for (const chapter of toc) { try { const paragraphs = await apiClient.getChapter(typedBook.book_id, chapter.id); console.log(` šŸ“„ Chapter "${chapter.title}": ${paragraphs.length} paragraphs`); // Insert paragraphs for (const paragraph of paragraphs) { db.insertParagraph(paragraph, typedBook.book_id, chapter.title); totalParagraphs++; } // Rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); } catch (chapterError) { console.error(` āŒ Error downloading chapter ${chapter.id}:`, chapterError); } } console.log(` āœ… Downloaded ${totalParagraphs} paragraphs`); } catch (bookError) { console.error(`āŒ Error downloading book ${typedBook.book_id}:`, bookError); } } console.log('šŸŽ‰ Content download complete!'); } catch (error) { console.error('āŒ Error downloading content:', error); process.exit(1); } finally { db.close(); } }); program .command('stats') .description('Show database statistics') .action(async () => { const db = new EGWDatabase(); try { const stats = db.getStats(); console.log('šŸ“Š Database Statistics:'); console.log(` Languages: ${stats.languages}`); console.log(` Books: ${stats.books}`); console.log(` Downloaded Books: ${stats.downloadedBooks}`); console.log(` Paragraphs: ${stats.paragraphs}`); const progress = db.getDownloadProgress(); if (progress.length > 0) { console.log('\nšŸ“ˆ Recent Download Progress:'); progress.slice(0, 5).forEach((p) => { console.log(` ${p.task_type}: ${p.completed_items}/${p.total_items || '?'} (${p.status})`); }); } } catch (error) { console.error('āŒ Error getting stats:', error); process.exit(1); } finally { db.close(); } }); program .command('quick-start') .description('Quick setup: download languages, sample books, and content') .action(async () => { console.log('šŸš€ Quick Start: Setting up EGW Writings database...\n'); const authManager = createAuthManager(); const apiClient = createApiClient(authManager); const db = new EGWDatabase(); try { // Step 1: Languages console.log('1ļøāƒ£ Downloading languages...'); const languages = await apiClient.getLanguages(); for (const lang of languages) { db.insertLanguage(lang.code, lang.name, lang.direction); } console.log(`āœ… ${languages.length} languages indexed\n`); // Step 2: Books (English only, first 20) console.log('2ļøāƒ£ Downloading sample books (English)...'); const folders = await apiClient.getFolders('en'); const booksFolder = folders.find(f => f.children?.find(c => c.name === 'Books'))?.children?.find(c => c.name === 'Books'); if (booksFolder) { const books = await apiClient.getBooksByFolder(booksFolder.folder_id); const sampleBooks = books.slice(0, 20); for (const book of sampleBooks) { db.insertBook(book); } console.log(`āœ… ${sampleBooks.length} books indexed\n`); // Step 3: Content (first 3 books only) console.log('3ļøāƒ£ Downloading sample content...'); const contentBooks = sampleBooks.slice(0, 3); for (const book of contentBooks) { console.log(` šŸ“– ${book.title}...`); try { const toc = await apiClient.getBookToc(book.book_id); const firstChapter = toc[0]; if (firstChapter) { const paragraphs = await apiClient.getChapter(book.book_id, firstChapter.id); for (const paragraph of paragraphs) { db.insertParagraph(paragraph, book.book_id, firstChapter.title); } console.log(` āœ… ${paragraphs.length} paragraphs from first chapter`); } // Rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { console.log(` āŒ Error: ${error}`); } } } console.log('\nšŸŽ‰ Quick start complete!'); const stats = db.getStats(); console.log('\nšŸ“Š Final Statistics:'); console.log(` Languages: ${stats.languages}`); console.log(` Books: ${stats.books}`); console.log(` Paragraphs: ${stats.paragraphs}`); console.log('\nšŸ’” Try: pnpm --filter local-server dev'); } catch (error) { console.error('āŒ Quick start failed:', error); process.exit(1); } finally { db.close(); } }); program.parse();