""" AI Content Generator Service - Main Application FastAPI Service für automatische H5P Content-Generierung """ from fastapi import FastAPI, File, UploadFile, Form, HTTPException, BackgroundTasks from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from typing import List, Optional from pydantic import BaseModel import os from datetime import datetime from app.services.claude_service import ClaudeService from app.services.youtube_service import YouTubeService from app.services.content_generator import ContentGenerator from app.services.material_analyzer import MaterialAnalyzer from app.models.generation_job import GenerationJob, JobStatus from app.utils.job_store import JobStore app = FastAPI( title="BreakPilot AI Content Generator", description="Automatische H5P Content-Generierung mit Claude AI", version="1.0.0" ) # CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Services claude_service = ClaudeService() youtube_service = YouTubeService() content_generator = ContentGenerator(claude_service, youtube_service) material_analyzer = MaterialAnalyzer() job_store = JobStore() # Models class GenerateContentRequest(BaseModel): topic: str description: Optional[str] = None target_grade: Optional[str] = None # z.B. "5-6" language: str = "de" class GenerationResponse(BaseModel): job_id: str status: str message: str class JobStatusResponse(BaseModel): job_id: str status: str progress: int message: str result: Optional[dict] = None error: Optional[str] = None created_at: datetime updated_at: datetime @app.get("/") async def root(): """Service Info""" return { "service": "AI Content Generator", "version": "1.0.0", "status": "running", "features": [ "Material Analysis", "Claude AI Integration", "YouTube Video Crawler", "8 H5P Content Types", "Background Jobs" ] } @app.get("/health") async def health(): """Health Check""" return { "status": "healthy", "claude_api": claude_service.is_configured(), "youtube_api": youtube_service.is_configured() } @app.post("/api/generate-content", response_model=GenerationResponse) async def generate_content( background_tasks: BackgroundTasks, topic: str = Form(...), description: Optional[str] = Form(None), target_grade: Optional[str] = Form("5-6"), files: List[UploadFile] = File(default=[]) ): """ Generiere H5P Content aus hochgeladenen Materialien Args: topic: Thema (z.B. "Das menschliche Auge") description: Zusätzliche Beschreibung/Lernziele target_grade: Klassenstufe (z.B. "5-6") files: Hochgeladene Materialien (PDF, Images, DOCX) Returns: job_id und Status für Tracking """ try: # Job erstellen job = GenerationJob( topic=topic, description=description, target_grade=target_grade, material_count=len(files) ) job_store.save(job) # Background Task starten background_tasks.add_task( process_content_generation, job.job_id, topic, description, target_grade, files ) return GenerationResponse( job_id=job.job_id, status="processing", message=f"Content-Generierung gestartet für Thema: {topic}" ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.get("/api/generation-status/{job_id}", response_model=JobStatusResponse) async def get_generation_status(job_id: str): """ Hole Status eines Content-Generierungs-Jobs Args: job_id: Job ID aus der generate-content Response Returns: Aktueller Status, Progress und ggf. Ergebnis """ job = job_store.get(job_id) if not job: raise HTTPException(status_code=404, detail="Job not found") return JobStatusResponse( job_id=job.job_id, status=job.status.value, progress=job.progress, message=job.message, result=job.result, error=job.error, created_at=job.created_at, updated_at=job.updated_at ) @app.get("/api/generated-content/{job_id}") async def get_generated_content(job_id: str): """ Hole das generierte Content-Paket Returns: Alle 8 generierten H5P Content-Typen """ job = job_store.get(job_id) if not job: raise HTTPException(status_code=404, detail="Job not found") if job.status != JobStatus.COMPLETED: raise HTTPException( status_code=400, detail=f"Job not completed yet. Current status: {job.status.value}" ) return { "job_id": job_id, "topic": job.topic, "content": job.result } @app.post("/api/youtube-search") async def search_youtube_videos( query: str = Form(...), max_results: int = Form(5) ): """ Suche passende YouTube Videos zum Thema Args: query: Suchbegriff max_results: Maximale Anzahl Ergebnisse Returns: Liste von Video-Infos mit Transkript-Verfügbarkeit """ try: videos = await youtube_service.search_videos(query, max_results) return {"videos": videos} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) async def process_content_generation( job_id: str, topic: str, description: Optional[str], target_grade: str, files: List[UploadFile] ): """Background Task für Content-Generierung""" job = job_store.get(job_id) try: # 1. Materialien analysieren job.update_progress(10, "Analysiere hochgeladene Materialien...") job_store.save(job) materials = [] for file in files: content = await file.read() analysis = await material_analyzer.analyze(file.filename, content) materials.append(analysis) # 2. YouTube Videos suchen job.update_progress(30, "Suche passende YouTube Videos...") job_store.save(job) videos = await youtube_service.search_videos(topic, max_results=3) # 3. Content generieren job.update_progress(50, "Generiere H5P Content mit Claude AI...") job_store.save(job) generated_content = await content_generator.generate_all_content_types( topic=topic, description=description, target_grade=target_grade, materials=materials, videos=videos ) # 4. Fertig job.complete(generated_content) job_store.save(job) except Exception as e: job.fail(str(e)) job_store.save(job) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8004)