Translaterpeed / app /api /routes_download.py
Ruhivig65's picture
Upload 4 files
077ab6a verified
"""
============================================
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")
@router.get("/{novel_id}/info")
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,
}
@router.get("/{novel_id}/txt")
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)),
},
)
@router.get("/{novel_id}/txt-hindi")
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)),
},
)
@router.get("/{novel_id}/chapters")
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
],
}