Spaces:
Sleeping
Sleeping
| """FastAPI application for DDGS API.""" | |
| import logging | |
| from typing import Any | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel, Field | |
| from ddgs import DDGS | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Create FastAPI app | |
| app = FastAPI( | |
| title="DDGS API", | |
| description="A FastAPI wrapper for the DDGS (Dux Distributed Global Search) library", | |
| version="1.0.0", | |
| docs_url="/docs", | |
| redoc_url="/redoc", | |
| ) | |
| # Add CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Pydantic models for request/response | |
| class TextSearchRequest(BaseModel): | |
| """Request model for search operations.""" | |
| query: str = Field(..., description="Search query") | |
| region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)") | |
| safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)") | |
| timelimit: str | None = Field(None, description="Time limit (d, w, m, y) or custom date range") | |
| max_results: int | None = Field(10, description="Maximum number of results to return") | |
| page: int = Field(1, description="Page number of results") | |
| backend: str = Field("auto", description="Search backend (auto, or specific engine)") | |
| class ImagesSearchRequest(BaseModel): | |
| """Request model for image search operations.""" | |
| query: str = Field(..., description="Image search query") | |
| region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)") | |
| safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)") | |
| timelimit: str | None = Field(None, description="Time limit (d, w, m, y) or custom date range") | |
| max_results: int | None = Field(10, description="Maximum number of results to return") | |
| page: int = Field(1, description="Page number of results") | |
| backend: str = Field("auto", description="Search backend (auto, or specific engine)") | |
| size: str | None = Field(None, description="Image size (Small, Medium, Large, Wallpaper)") | |
| color: str | None = Field( | |
| None, | |
| description="Image color (Monochrome, Red, Orange, Yellow, Green, Blue, Purple, Pink, Brown, Black, Gray, Teal, White)", # noqa: E501 | |
| ) | |
| type_image: str | None = Field(None, description="Image type (photo, clipart, gif, transparent, line)") | |
| layout: str | None = Field(None, description="Image layout (Square, Tall, Wide)") | |
| license_image: str | None = Field( | |
| None, description="Image license (any, Public, Share, ShareCommercially, Modify, ModifyCommercially)" | |
| ) | |
| class NewsSearchRequest(BaseModel): | |
| """Request model for search operations.""" | |
| query: str = Field(..., description="Search query") | |
| region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)") | |
| safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)") | |
| timelimit: str | None = Field(None, description="Time limit (d, w, m, y) or custom date range") | |
| max_results: int | None = Field(10, description="Maximum number of results to return") | |
| page: int = Field(1, description="Page number of results") | |
| backend: str = Field("auto", description="Search backend (auto, or specific engine)") | |
| class VideosSearchRequest(BaseModel): | |
| """Request model for video search operations.""" | |
| query: str = Field(..., description="Video search query") | |
| region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)") | |
| safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)") | |
| timelimit: str | None = Field(None, description="Time limit (d, w, m) or custom date range") | |
| max_results: int | None = Field(10, description="Maximum number of results to return") | |
| page: int = Field(1, description="Page number of results") | |
| backend: str = Field("auto", description="Search backend (auto, or specific engine)") | |
| resolution: str | None = Field(None, description="Video resolution (high, standard)") | |
| duration: str | None = Field(None, description="Video duration (short, medium, long)") | |
| license_videos: str | None = Field(None, description="Video license (creativeCommon, youtube)") | |
| class BooksSearchRequest(BaseModel): | |
| """Request model for book search operations.""" | |
| query: str = Field(..., description="Books search query") | |
| max_results: int | None = Field(10, description="Maximum number of results to return") | |
| page: int = Field(1, description="Page number of results") | |
| backend: str = Field("auto", description="Search backend (auto, or specific engine)") | |
| class SearchResponse(BaseModel): | |
| """Response model for search operations.""" | |
| results: list[dict[str, Any]] | |
| class HealthResponse(BaseModel): | |
| """Response model for health check.""" | |
| status: str | |
| version: str | |
| service: str | |
| async def root() -> HealthResponse: | |
| """Root endpoint with basic service information.""" | |
| return HealthResponse(status="healthy", version="1.0.0", service="DDGS API") | |
| async def health_check() -> HealthResponse: | |
| """Health check endpoint.""" | |
| return HealthResponse(status="healthy", version="1.0.0", service="DDGS API") | |
| async def search_text(request: TextSearchRequest) -> SearchResponse: | |
| """Perform a text search.""" | |
| try: | |
| results = DDGS().text( | |
| query=request.query, | |
| region=request.region, | |
| safesearch=request.safesearch, | |
| timelimit=request.timelimit, | |
| max_results=request.max_results, | |
| page=request.page, | |
| backend=request.backend, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in text search: %s", e) | |
| raise HTTPException(status_code=500, detail=f"Search failed: {e!s}") from e | |
| async def search_text_get( | |
| query: str, | |
| region: str = "us-en", | |
| safesearch: str = "moderate", | |
| timelimit: str | None = None, | |
| max_results: int = 10, | |
| page: int = 1, | |
| backend: str = "auto", | |
| ) -> SearchResponse: | |
| """Perform a text search via GET request.""" | |
| try: | |
| results = DDGS().text( | |
| query=query, | |
| region=region, | |
| safesearch=safesearch, | |
| timelimit=timelimit, | |
| max_results=max_results, | |
| page=page, | |
| backend=backend, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in text search (GET): %s", e) | |
| raise HTTPException(status_code=500, detail=f"Search failed: {e!s}") from e | |
| async def search_images(request: ImagesSearchRequest) -> SearchResponse: | |
| """Perform an image search.""" | |
| try: | |
| results = DDGS().images( | |
| query=request.query, | |
| region=request.region, | |
| safesearch=request.safesearch, | |
| timelimit=request.timelimit, | |
| max_results=request.max_results, | |
| page=request.page, | |
| backend=request.backend, | |
| size=request.size, | |
| color=request.color, | |
| type_image=request.type_image, | |
| layout=request.layout, | |
| license_image=request.license_image, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in image search: %s", e) | |
| raise HTTPException(status_code=500, detail=f"Image search failed: {e!s}") from e | |
| async def search_images_get( | |
| query: str, | |
| region: str = "us-en", | |
| safesearch: str = "moderate", | |
| timelimit: str | None = None, | |
| max_results: int = 10, | |
| page: int = 1, | |
| backend: str = "auto", | |
| size: str | None = None, | |
| color: str | None = None, | |
| type_image: str | None = None, | |
| layout: str | None = None, | |
| license_image: str | None = None, | |
| ) -> SearchResponse: | |
| """Perform an image search via GET request.""" | |
| try: | |
| results = DDGS().images( | |
| query=query, | |
| region=region, | |
| safesearch=safesearch, | |
| timelimit=timelimit, | |
| max_results=max_results, | |
| page=page, | |
| backend=backend, | |
| size=size, | |
| color=color, | |
| type_image=type_image, | |
| layout=layout, | |
| license_image=license_image, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in image search (GET): %s", e) | |
| raise HTTPException(status_code=500, detail=f"Image search failed: {e!s}") from e | |
| async def search_news(request: NewsSearchRequest) -> SearchResponse: | |
| """Perform a news search.""" | |
| try: | |
| results = DDGS().news( | |
| query=request.query, | |
| region=request.region, | |
| safesearch=request.safesearch, | |
| timelimit=request.timelimit, | |
| max_results=request.max_results, | |
| page=request.page, | |
| backend=request.backend, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in news search: %s", e) | |
| raise HTTPException(status_code=500, detail=f"News search failed: {e!s}") from e | |
| async def search_news_get( | |
| query: str, | |
| region: str = "us-en", | |
| safesearch: str = "moderate", | |
| timelimit: str | None = None, | |
| max_results: int = 10, | |
| page: int = 1, | |
| backend: str = "auto", | |
| ) -> SearchResponse: | |
| """Perform a news search via GET request.""" | |
| try: | |
| results = DDGS().news( | |
| query=query, | |
| region=region, | |
| safesearch=safesearch, | |
| timelimit=timelimit, | |
| max_results=max_results, | |
| page=page, | |
| backend=backend, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in news search (GET): %s", e) | |
| raise HTTPException(status_code=500, detail=f"News search failed: {e!s}") from e | |
| async def search_videos(request: VideosSearchRequest) -> SearchResponse: | |
| """Perform a video search.""" | |
| try: | |
| results = DDGS().videos( | |
| query=request.query, | |
| region=request.region, | |
| safesearch=request.safesearch, | |
| timelimit=request.timelimit, | |
| max_results=request.max_results, | |
| page=request.page, | |
| backend=request.backend, | |
| resolution=request.resolution, | |
| duration=request.duration, | |
| license_videos=request.license_videos, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in video search: %s", e) | |
| raise HTTPException(status_code=500, detail=f"Video search failed: {e!s}") from e | |
| async def search_videos_get( | |
| query: str, | |
| region: str = "us-en", | |
| safesearch: str = "moderate", | |
| timelimit: str | None = None, | |
| max_results: int = 10, | |
| page: int = 1, | |
| backend: str = "auto", | |
| resolution: str | None = None, | |
| duration: str | None = None, | |
| license_videos: str | None = None, | |
| ) -> SearchResponse: | |
| """Perform a video search via GET request.""" | |
| try: | |
| results = DDGS().videos( | |
| query=query, | |
| region=region, | |
| safesearch=safesearch, | |
| timelimit=timelimit, | |
| max_results=max_results, | |
| page=page, | |
| backend=backend, | |
| resolution=resolution, | |
| duration=duration, | |
| license_videos=license_videos, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in video search (GET): %s", e) | |
| raise HTTPException(status_code=500, detail=f"Video search failed: {e!s}") from e | |
| async def search_books(request: BooksSearchRequest) -> SearchResponse: | |
| """Perform a book search.""" | |
| try: | |
| results = DDGS().books( | |
| query=request.query, | |
| max_results=request.max_results, | |
| page=request.page, | |
| backend=request.backend, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in book search: %s", e) | |
| raise HTTPException(status_code=500, detail=f"Book search failed: {e!s}") from e | |
| async def search_books_get( | |
| query: str, | |
| max_results: int = 10, | |
| page: int = 1, | |
| backend: str = "auto", | |
| ) -> SearchResponse: | |
| """Perform a book search via GET request.""" | |
| try: | |
| results = DDGS().books( | |
| query=query, | |
| max_results=max_results, | |
| page=page, | |
| backend=backend, | |
| ) | |
| return SearchResponse(results=results) | |
| except Exception as e: | |
| logger.warning("Error in book search (GET): %s", e) | |
| raise HTTPException(status_code=500, detail=f"Book search failed: {e!s}") from e | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=8000) # noqa: S104 | |