feat: Complete frontend-backend integration
- Created 4 new API endpoints in anime.py: * /api/v1/anime/status - Get library status * /api/v1/anime/add - Add new series * /api/v1/anime/download - Download folders * /api/v1/anime/process/locks - Check process locks - Updated frontend API calls in app.js to use correct endpoints - Cleaned up instructions.md by removing completed tasks - Added comprehensive integration documentation All tests passing. Core user workflows (list, search, add, download) now fully functional.
This commit is contained in:
@@ -8,6 +8,88 @@ from src.server.utils.dependencies import get_series_app, require_auth
|
||||
router = APIRouter(prefix="/api/v1/anime", tags=["anime"])
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
async def get_anime_status(
|
||||
_auth: dict = Depends(require_auth),
|
||||
series_app: Any = Depends(get_series_app),
|
||||
) -> dict:
|
||||
"""Get anime library status information.
|
||||
|
||||
Args:
|
||||
_auth: Ensures the caller is authenticated (value unused)
|
||||
series_app: Core `SeriesApp` instance provided via dependency
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Status information including directory and series count
|
||||
|
||||
Raises:
|
||||
HTTPException: If status retrieval fails
|
||||
"""
|
||||
try:
|
||||
directory = getattr(series_app, "directory", "") if series_app else ""
|
||||
|
||||
# Get series count
|
||||
series_count = 0
|
||||
if series_app and hasattr(series_app, "List"):
|
||||
series = series_app.List.GetList()
|
||||
series_count = len(series) if series else 0
|
||||
|
||||
return {
|
||||
"directory": directory,
|
||||
"series_count": series_count
|
||||
}
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to get status: {str(exc)}",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get("/process/locks")
|
||||
async def get_process_locks(
|
||||
_auth: dict = Depends(require_auth),
|
||||
series_app: Any = Depends(get_series_app),
|
||||
) -> dict:
|
||||
"""Get process lock status for rescan and download operations.
|
||||
|
||||
Args:
|
||||
_auth: Ensures the caller is authenticated (value unused)
|
||||
series_app: Core `SeriesApp` instance provided via dependency
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Lock status information
|
||||
|
||||
Raises:
|
||||
HTTPException: If lock status retrieval fails
|
||||
"""
|
||||
try:
|
||||
locks = {
|
||||
"rescan": {"is_locked": False},
|
||||
"download": {"is_locked": False}
|
||||
}
|
||||
|
||||
# Check if SeriesApp has lock status methods
|
||||
if series_app:
|
||||
if hasattr(series_app, "isRescanning"):
|
||||
locks["rescan"]["is_locked"] = series_app.isRescanning()
|
||||
if hasattr(series_app, "isDownloading"):
|
||||
locks["download"]["is_locked"] = series_app.isDownloading()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"locks": locks
|
||||
}
|
||||
except Exception as exc:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(exc),
|
||||
"locks": {
|
||||
"rescan": {"is_locked": False},
|
||||
"download": {"is_locked": False}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AnimeSummary(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
@@ -96,6 +178,19 @@ async def trigger_rescan(series_app: Any = Depends(get_series_app)) -> dict:
|
||||
) from exc
|
||||
|
||||
|
||||
class AddSeriesRequest(BaseModel):
|
||||
"""Request model for adding a new series."""
|
||||
|
||||
link: str
|
||||
name: str
|
||||
|
||||
|
||||
class DownloadFoldersRequest(BaseModel):
|
||||
"""Request model for downloading missing episodes from folders."""
|
||||
|
||||
folders: List[str]
|
||||
|
||||
|
||||
class SearchRequest(BaseModel):
|
||||
"""Request model for anime search with validation."""
|
||||
|
||||
@@ -190,6 +285,95 @@ async def search_anime(
|
||||
) from exc
|
||||
|
||||
|
||||
@router.post("/add")
|
||||
async def add_series(
|
||||
request: AddSeriesRequest,
|
||||
_auth: dict = Depends(require_auth),
|
||||
series_app: Any = Depends(get_series_app),
|
||||
) -> dict:
|
||||
"""Add a new series to the library.
|
||||
|
||||
Args:
|
||||
request: Request containing the series link and name
|
||||
_auth: Ensures the caller is authenticated (value unused)
|
||||
series_app: Core `SeriesApp` instance provided via dependency
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Status payload with success message
|
||||
|
||||
Raises:
|
||||
HTTPException: If adding the series fails
|
||||
"""
|
||||
try:
|
||||
if not hasattr(series_app, "AddSeries"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
detail="Add series functionality not available",
|
||||
)
|
||||
|
||||
result = series_app.AddSeries(request.link, request.name)
|
||||
|
||||
if result:
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Successfully added series: {request.name}"
|
||||
}
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Failed to add series - series may already exist",
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to add series: {str(exc)}",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.post("/download")
|
||||
async def download_folders(
|
||||
request: DownloadFoldersRequest,
|
||||
_auth: dict = Depends(require_auth),
|
||||
series_app: Any = Depends(get_series_app),
|
||||
) -> dict:
|
||||
"""Start downloading missing episodes from the specified folders.
|
||||
|
||||
Args:
|
||||
request: Request containing list of folder names
|
||||
_auth: Ensures the caller is authenticated (value unused)
|
||||
series_app: Core `SeriesApp` instance provided via dependency
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Status payload with success message
|
||||
|
||||
Raises:
|
||||
HTTPException: If download initiation fails
|
||||
"""
|
||||
try:
|
||||
if not hasattr(series_app, "Download"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
detail="Download functionality not available",
|
||||
)
|
||||
|
||||
# Call Download with the folders and a no-op callback
|
||||
series_app.Download(request.folders, lambda *args, **kwargs: None)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Download started for {len(request.folders)} series"
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to start download: {str(exc)}",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get("/{anime_id}", response_model=AnimeDetail)
|
||||
async def get_anime(
|
||||
anime_id: str,
|
||||
|
||||
Reference in New Issue
Block a user