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:
2025-10-24 10:27:07 +02:00
parent 77da614091
commit 0fd9c424cd
4 changed files with 376 additions and 19 deletions

View File

@@ -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,