Spaces:
Sleeping
Sleeping
| """ | |
| ============================================ | |
| Download Routes | |
| - English .txt → chapter.content | |
| - Hindi .txt → chapter.content_hindi (already translated during scraping) | |
| ============================================ | |
| """ | |
| import io | |
| import logging | |
| from fastapi import APIRouter, Depends, HTTPException | |
| from fastapi.responses import StreamingResponse | |
| from sqlalchemy.ext.asyncio import AsyncSession | |
| from app.database.connection import get_db_session | |
| from app.database.crud import ( | |
| get_novel_by_id, | |
| get_chapters_for_novel, | |
| get_chapter_count, | |
| get_total_word_count, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter(prefix="/api/download", tags=["Download"]) | |
| def _build_txt(novel, chapters, lang="english") -> bytes: | |
| """Build .txt file content for given language.""" | |
| lines = [] | |
| if lang == "hindi": | |
| title = novel.title_hindi or novel.title | |
| else: | |
| title = novel.title | |
| lines.append("=" * 60) | |
| lines.append(f" {title}") | |
| if lang == "hindi": | |
| lines.append(f" ({novel.title})") | |
| lines.append("=" * 60) | |
| lines.append(f" Source: {novel.url}") | |
| lines.append(f" Chapters: {len(chapters)}") | |
| total_words = sum(ch.word_count for ch in chapters) | |
| lines.append(f" Total Words: {total_words:,}") | |
| lines.append(f" Language: {'Hindi 🇮🇳' if lang == 'hindi' else 'English'}") | |
| lines.append(f" Generated by Novel Scraper Pro") | |
| lines.append("=" * 60) | |
| lines.append("") | |
| for chapter in chapters: | |
| if lang == "hindi": | |
| ch_title = chapter.title_hindi or chapter.title | |
| ch_content = chapter.content_hindi or chapter.content | |
| ch_label = f"अध्याय {chapter.chapter_number}" | |
| else: | |
| ch_title = chapter.title | |
| ch_content = chapter.content | |
| ch_label = f"Chapter {chapter.chapter_number}" | |
| lines.append("-" * 50) | |
| lines.append(f" {ch_label}: {ch_title}") | |
| lines.append("-" * 50) | |
| lines.append("") | |
| lines.append(ch_content) | |
| lines.append("") | |
| lines.append("=" * 60) | |
| lines.append(" उपन्यास समाप्त" if lang == "hindi" else " END OF NOVEL") | |
| lines.append("=" * 60) | |
| return "\n".join(lines).encode("utf-8") | |
| async def get_download_info(novel_id: int, db: AsyncSession = Depends(get_db_session)): | |
| novel = await get_novel_by_id(db, novel_id) | |
| if not novel: | |
| raise HTTPException(status_code=404, detail="Novel not found") | |
| chapter_count = await get_chapter_count(db, novel_id) | |
| word_count = await get_total_word_count(db, novel_id) | |
| return { | |
| "novel_id": novel_id, | |
| "title": novel.title, | |
| "status": novel.status.value, | |
| "chapter_count": chapter_count, | |
| "word_count": word_count, | |
| "estimated_pages": word_count // 250 if word_count else 0, | |
| "downloadable": chapter_count > 0, | |
| } | |
| async def download_english(novel_id: int, db: AsyncSession = Depends(get_db_session)): | |
| """Download novel as English .txt""" | |
| novel = await get_novel_by_id(db, novel_id) | |
| if not novel: | |
| raise HTTPException(status_code=404, detail="Novel not found") | |
| chapters = await get_chapters_for_novel(db, novel_id) | |
| if not chapters: | |
| raise HTTPException(status_code=404, detail="No chapters found!") | |
| file_bytes = _build_txt(novel, chapters, lang="english") | |
| safe = "".join(c for c in novel.title if c.isalnum() or c in ' -_').strip()[:80] | |
| return StreamingResponse( | |
| io.BytesIO(file_bytes), | |
| media_type="text/plain; charset=utf-8", | |
| headers={ | |
| "Content-Disposition": f'attachment; filename="{safe}_English.txt"', | |
| "Content-Length": str(len(file_bytes)), | |
| }, | |
| ) | |
| async def download_hindi(novel_id: int, db: AsyncSession = Depends(get_db_session)): | |
| """ | |
| Download novel as Hindi .txt | |
| Translation already done during scraping — instant download! | |
| """ | |
| novel = await get_novel_by_id(db, novel_id) | |
| if not novel: | |
| raise HTTPException(status_code=404, detail="Novel not found") | |
| chapters = await get_chapters_for_novel(db, novel_id) | |
| if not chapters: | |
| raise HTTPException(status_code=404, detail="No chapters found!") | |
| hindi_chapters = [ch for ch in chapters if ch.content_hindi] | |
| if not hindi_chapters: | |
| raise HTTPException( | |
| status_code=404, | |
| detail="Hindi translation nahi mili! Purane chapters dobara scrape karo." | |
| ) | |
| file_bytes = _build_txt(novel, hindi_chapters, lang="hindi") | |
| safe = "".join(c for c in novel.title if c.isalnum() or c in ' -_').strip()[:80] | |
| logger.info(f"Hindi download: '{novel.title}' - {len(hindi_chapters)} chapters") | |
| return StreamingResponse( | |
| io.BytesIO(file_bytes), | |
| media_type="text/plain; charset=utf-8", | |
| headers={ | |
| "Content-Disposition": f'attachment; filename="{safe}_Hindi.txt"', | |
| "Content-Length": str(len(file_bytes)), | |
| }, | |
| ) | |
| async def list_chapters(novel_id: int, db: AsyncSession = Depends(get_db_session)): | |
| novel = await get_novel_by_id(db, novel_id) | |
| if not novel: | |
| raise HTTPException(status_code=404, detail="Novel not found") | |
| chapters = await get_chapters_for_novel(db, novel_id) | |
| return { | |
| "novel_id": novel_id, | |
| "title": novel.title, | |
| "total_chapters": len(chapters), | |
| "chapters": [ | |
| { | |
| "number": ch.chapter_number, | |
| "title": ch.title, | |
| "title_hindi": ch.title_hindi, | |
| "word_count": ch.word_count, | |
| "hindi_available": bool(ch.content_hindi), | |
| "url": ch.url, | |
| "scraped_at": ch.scraped_at.isoformat() if ch.scraped_at else None, | |
| } | |
| for ch in chapters | |
| ], | |
| } | |