# ๐Ÿ“š Ohara โ€” Personal Novel Library A self-hosted static web library for your scraped novels. Scrape โ†’ export JSON/HTML โ†’ push to GitHub/Vercel Blob โ†’ read anywhere. --- ## Repo structure ```text ohara/ โ”œโ”€โ”€ docs/ โ† GitHub Pages source (deploy this folder) โ”‚ โ”œโ”€โ”€ index.html โ† Novel grid / homepage โ”‚ โ”œโ”€โ”€ novel.html โ† Novel detail + chapter list โ”‚ โ”œโ”€โ”€ chapter.html โ† Chapter reader โ”‚ โ”œโ”€โ”€ style.css โ”‚ โ”œโ”€โ”€ app.js โ”‚ โ”œโ”€โ”€ read/ โ† Static HTML exported chapters โ”‚ โ””โ”€โ”€ data/ โ”‚ โ”œโ”€โ”€ index.json โ† Master novel list (auto-generated) โ”‚ โ””โ”€โ”€ / โ”‚ โ”œโ”€โ”€ meta.json โ† Novel metadata + chapter index โ”‚ โ””โ”€โ”€ chapters/ โ”‚ โ”œโ”€โ”€ 1.json โ”‚ โ”œโ”€โ”€ 2.json โ”‚ โ””โ”€โ”€ โ€ฆ โ”œโ”€โ”€ output/ โ† EPUB files (not committed, gitignored) โ”œโ”€โ”€ ohara.py โ† The scraper (aka scrape_catalog.py) โ”œโ”€โ”€ scraped_novels.txt โ† Resume tracking (auto-created) โ””โ”€โ”€ failed_chapters.txt โ† Failed chapter log (auto-created) ``` --- ## 1 ยท Install dependencies ```bash pip install requests beautifulsoup4 ebooklib python-dotenv ``` --- ## 2 ยท Scrape novels The scraper (`ohara.py`) can write EPUBs to `output/`, JSON to `docs/data/`, and static HTML to `docs/read/`. It can also upload directly to Vercel Blob using the `--cloud` flags. ### Basic Scraping ```bash # Scrape the default listing (latest novels) python ohara.py # Scrape a specific single novel python ohara.py --novel shadow-slave # Scrape a specific genre or sort page python ohara.py --listing https://freewebnovel.com/genre/Fantasy # Only first 3 pages of a listing python ohara.py --pages 3 # Skip novels already completed in previous runs python ohara.py --resume # Exclude specific genres from being scraped python ohara.py --exclude-genre Yaoi Yuri "Shounen Ai" ``` ### Maintenance and Updates ```bash # Check every local novel for new chapters python ohara.py --update # Run continuously, checking for updates every 60 minutes python ohara.py --watch --interval 60 # Automatically commit and push to GitHub after updates finish python ohara.py --update --auto-push # Retry any chapters that failed python ohara.py --retry-failed # Fetch tags for existing novels python ohara.py --fetch-tags # Print a summary of the local and cloud library python ohara.py --status ``` ### Output Options ```bash # Only export JSON (no epub) โ€” useful for site-only updates python ohara.py --no-epub # Only export epub (skip site JSON) python ohara.py --no-site # Generate static HTML files for SEO and non-JS clients python ohara.py --html # Rebuild all static HTML files for existing novels python ohara.py --rebuild-html ``` ### Cloud Storage (Vercel Blob) Requires `BLOB_READ_WRITE_TOKEN` in your `.env` file. ```bash # Upload scraped data to Vercel Blob alongside local files python ohara.py --cloud # Upload directly to Vercel Blob ONLY (no local files written) python ohara.py --cloud-only # Upload your existing local library to Vercel Blob python ohara.py --upload-existing ``` ### Deletion ```bash # Delete a specific novel locally (and optionally in the cloud) python ohara.py --delete-novel shadow-slave # Delete all novels of specific genres (use --dry-run to preview) python ohara.py --delete-genre Yaoi Yuri --dry-run ``` ### Speed & Proxy Tuning The scraper supports proxy rotation, User-Agent rotation, and adjustable concurrency to speed up scraping and avoid IP-based rate limiting. ```bash # Fast mode โ€” 5x speed boost (reduces delays, increases workers to 16) python ohara.py --novel URL --fast # Fast mode + proxy rotation (recommended for heavy scraping) python ohara.py --novel URL --fast --proxy-file proxies.txt # Custom worker count and delay python ohara.py --novel URL --workers 24 --delay 1 # Full speed bulk update with proxies python ohara.py --update --fast --proxy-file proxies.txt --no-epub --auto-push ``` | Flag | Default | Description | |------|---------|-------------| | `--fast` | off | Speed preset: delay=1s, page_delay=0.5s, novel_delay=1s, workers=16 | | `--workers N` | 8 | Parallel chapter-download threads per novel | | `--delay SECS` | 5 | Seconds to wait after each chapter request (per-thread) | | `--proxy-file FILE` | none | Path to a proxy list file for round-robin rotation | > **Note:** `--workers` and `--delay` override `--fast` values when used together. **Proxy file format** (`proxies.txt`): ```text # One proxy per line โ€” lines starting with '#' are ignored http://123.45.67.89:8080 socks5://proxy.example.com:1080 host:port http://user:pass@proxy.example.com:3128 ``` > **Warning:** Using `--fast` without proxies may trigger anti-bot blocks (429 errors). Combine with `--proxy-file` for best results. --- ## 3 ยท Deploy to GitHub Pages ### First time ```bash git init git add docs/ ohara.py README.md git commit -m "initial commit" git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO.git git push -u origin main ``` Then in GitHub โ†’ **Settings โ†’ Pages**: - Source: `Deploy from a branch` - Branch: `main` - Folder: `/docs` - Save Your site will be live at `https://YOUR_USERNAME.github.io/YOUR_REPO/` ### Updating with new chapters ```bash # Scrape new content python ohara.py --update # Push the updated data files git add docs/data/ docs/read/ git commit -m "update novels $(date +%Y-%m-%d)" git push ``` Or simply use `python ohara.py --update --auto-push` to do this automatically! --- ## 4 ยท Gitignore Create a `.gitignore` so you don't accidentally commit the large epub files or sensitive environment variables: ```text output/ scraped_novels.txt failed_chapters.txt __pycache__/ *.pyc .env ``` --- ## 5 ยท Site features | Feature | Detail | |---------|--------| | Novel grid | Cover image, chapter count, last updated | | Live search | Filter novels by name instantly | | Content Filter| Password-protected filter to lock/unlock specific novel visibility | | Chapter list | Paginated list (100 per page) | | Previous / Next | Navigate between chapters | | Merge Next 10 | Load and append the next 10 chapters inline without leaving the page | | Font size | Aโˆ’ / A+ controls saved to localStorage | | Dark theme | Easy on the eyes for long reading sessions | > **Note on Content Filter:** The filter is locked by default and requires a password to unlock. To change the password, search for `prompt("Enter password to unlock:")` in `docs/index.html` and change the string it compares against. --- ## Data format reference ### `docs/data/index.json` ```json { "novels": [ { "slug": "the-beginning-after-the-end", "title": "The Beginning After the End", "cover": "https://โ€ฆ/cover.jpg", "totalChapters": 450, "lastUpdated": "2025-01-15" } ] } ``` ### `docs/data//meta.json` ```json { "slug": "the-beginning-after-the-end", "title": "The Beginning After the End", "cover": "https://โ€ฆ/cover.jpg", "source": "https://freewebnovel.com/novel/the-beginning-after-the-end", "totalChapters": 450, "lastUpdated": "2025-01-15", "tags": ["Action", "Adventure"], "chapters": [ { "num": 1, "title": "Chapter 1: Prologue" }, { "num": 2, "title": "Chapter 2: A New World" } ] } ``` ### `docs/data//chapters/.json` ```json { "num": 1, "title": "Chapter 1: Prologue", "content": "Paragraph one.\n\nParagraph two.\n\nParagraph three." } ```