A bilingual reader application that helps you read books in two languages. Built with React and deployed as a Cloudflare Worker with D1 database integration.
- Book Shelf: Browse your bilingual book collection with an elegant card-based interface
- Bilingual Reading: Switch between original and translated text by clicking paragraphs
- Chapter Navigation: Manual chapter navigation with curved arrow buttons
- Responsive Design: Works on both desktop and mobile devices
- Multiple Languages: Support for various language pairs (EN-ZH, EN-ES, EN-FR, etc.)
- User Authentication: Google OAuth login for personalized book management
- Web Upload: Upload EPUB files directly through the web interface (admin only)
- Book Privacy: Public books visible to all, private books visible only to owners
- Node.js (v16 or higher) and npm installed
- Cloudflare account (for deployment)
- Wrangler CLI installed globally:
npm install -g wrangler
-
Clone the repository:
git clone <repository-url> cd ovid
-
Install dependencies:
npm install
-
Configure the project:
# Copy example files to create local configs cp .env.example .env cp wrangler.toml.example wrangler.toml # Edit wrangler.toml and replace "your-database-id-here" # with your actual Cloudflare D1 database ID # Edit .env if you want to use book translation features
-
Set up local database:
# Create complete database schema (includes all tables, indexes, and migrations) npm run db:local -- --file database/schema.sql # Add sample data (optional) - includes 4 books with chapters and content npm run db:local -- --file database/sample_data.sql
-
Start the local development server:
npm run preview
-
Open your browser to http://localhost:8787
The main page will show your book shelf with available books. Click any book to start reading.
npm run preview- Start local development server with Cloudflare Workersnpm run build- Build React application for productionnpm run deploy- Deploy to Cloudflare Workers (requires authentication)npm run db:local- Execute SQL commands on local databasenpm run db:remote- Execute SQL commands on remote databaseyarn import-book- Import EPUB/TXT books with translationyarn list-books:local- List all books in local database with timestampsyarn list-books:remote- List all books in remote database with timestampsyarn remove-book:local- Remove books from local database by UUID (with confirmation)yarn remove-book:remote- Remove books from remote database by UUID (with confirmation)yarn sync-remote-book- Sync a locally imported book to the remote D1 (ensures schema first)npm start- Start React development server (frontend only)npm test- Run tests
src/
├── components/
│ ├── BilingualReader.tsx # Main reading interface
│ ├── BookShelf.tsx # Book library/shelf component
│ └── *.css # Component styles
├── worker/
│ └── index.ts # Cloudflare Worker backend
└── App.tsx # Main React application
database/
├── schema.sql # Complete database schema (includes all migrations)
└── sample_data.sql # Sample book data
wrangler.toml.example # Cloudflare Workers config template (committed)
wrangler.toml # Your local config with real IDs (not committed)
.env.example # Environment variables template (committed)
.env # Your local environment variables (not committed)
- books: Book metadata (title, author, language_pair, uuid)
- chapters: Chapter information linked to books
- content_items: Individual paragraphs with bilingual text
Ovid includes an automated book import system that supports EPUB and TXT files with automatic translation:
# Import EPUB with Chinese translation
yarn import-book -- --file="book.epub" --target="zh" --provider="openai"
# Import TXT with Spanish translation
yarn import-book -- --file="book.txt" --target="es" --title="Book Title" --author="Author Name"
# List all books in local database
yarn list-books:local
# List all books in remote database
yarn list-books:remote
# Remove a book from local database by UUID (with confirmation prompt)
yarn remove-book:local -- --uuid="book-uuid-here"
# Remove a book from remote database by UUID (with confirmation prompt)
yarn remove-book:remote -- --uuid="book-uuid-here"
# Sync a locally imported book to remote D1 (ensures schema first)
yarn sync-remote-book -- --uuid="book-uuid-here"
# Available target languages: zh (Chinese), es (Spanish), fr (French), de (German), ja (Japanese), ko (Korean), ru (Russian)
# Available providers: openai, google, deeplFor OpenAI (Recommended):
Add to your .env file:
OPENAI_API_KEY=sk-your-openai-api-key-here
OPENAI_API_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-miniFor OpenAI-Compatible APIs: You can use other providers like OpenRouter, Together AI, or local models:
OPENAI_API_KEY=your-api-key-here
OPENAI_API_BASE_URL=https://openrouter.ai/api/v1
OPENAI_MODEL=anthropic/claude-3-haikuModel Options:
gpt-4o-mini- Cost-efficient, good quality (recommended)gpt-4o- Higher quality, more expensiveanthropic/claude-3-haiku- Via OpenRoutermeta-llama/llama-3.1-70b-instruct- Via Together AI
The import system will:
- Parse the book file (EPUB/TXT) and extract chapters
- Translate all content using your chosen API provider
- Import the bilingual content into your database with a unique UUID
- Display the book automatically in your shelf
Cost Estimate: ~$3-15 per average book using OpenAI GPT-4o-mini
For advanced users, you can also manually import bilingual content:
- Insert book metadata with a unique UUID
- Add chapter information and content items
- Books will automatically appear in the shelf
-
Authenticate with Cloudflare:
wrangler auth login
-
Deploy to production:
npm run deploy
For Cloudflare Worker (set via wrangler secret put or in wrangler.toml):
GOOGLE_OAUTH_CLIENT_ID- Google OAuth client ID (required for authentication)GOOGLE_OAUTH_CLIENT_SECRET- Google OAuth client secret (required for authentication)APP_URL- Application URL, e.g.,https://lib.jrd.pub(required)OPENAI_API_KEY- OpenAI API key for web upload translation (required for web upload)OPENAI_API_BASE_URL- OpenAI API base URL (optional, defaults to https://api.openai.com/v1)OPENAI_MODEL- Translation model to use (optional, defaults to gpt-4o-mini)
For local development and CLI scripts (set in .env):
CLOUDFLARE_D1_DATABASE_ID- Your D1 database ID for local developmentCLOUDFLARE_ACCOUNT_ID- Your Cloudflare account ID (required for remote operations)CLOUDFLARE_API_TOKEN- Your Cloudflare API token (required for remote database operations)OPENAI_API_KEY- OpenAI API key for book translationOPENAI_API_BASE_URL- OpenAI API base URL (optional)OPENAI_MODEL- Translation model to use (optional)
- Fork the repository
- Create a feature branch
- Make your changes
- Ensure tests pass
- Submit a pull request
MIT License - see the LICENSE file for details