feat: Implement pagination for extractions and playlists with total count in responses
This commit is contained in:
@@ -38,6 +38,16 @@ class ExtractionInfo(TypedDict):
|
||||
updated_at: str
|
||||
|
||||
|
||||
class PaginatedExtractionsResponse(TypedDict):
|
||||
"""Type definition for paginated extractions response."""
|
||||
|
||||
extractions: list[ExtractionInfo]
|
||||
total: int
|
||||
page: int
|
||||
limit: int
|
||||
total_pages: int
|
||||
|
||||
|
||||
class ExtractionService:
|
||||
"""Service for extracting audio from external services using yt-dlp."""
|
||||
|
||||
@@ -565,17 +575,22 @@ class ExtractionService:
|
||||
sort_by: str = "created_at",
|
||||
sort_order: str = "desc",
|
||||
status_filter: str | None = None,
|
||||
) -> list[ExtractionInfo]:
|
||||
page: int = 1,
|
||||
limit: int = 50,
|
||||
) -> PaginatedExtractionsResponse:
|
||||
"""Get all extractions for a user with filtering, search, and sorting."""
|
||||
extraction_user_tuples = await self.extraction_repo.get_user_extractions_filtered(
|
||||
offset = (page - 1) * limit
|
||||
extraction_user_tuples, total_count = await self.extraction_repo.get_user_extractions_filtered(
|
||||
user_id=user_id,
|
||||
search=search,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
status_filter=status_filter,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
return [
|
||||
extractions = [
|
||||
{
|
||||
"id": extraction.id
|
||||
or 0, # Should never be None for existing extraction
|
||||
@@ -594,22 +609,37 @@ class ExtractionService:
|
||||
for extraction, user in extraction_user_tuples
|
||||
]
|
||||
|
||||
total_pages = (total_count + limit - 1) // limit # Ceiling division
|
||||
|
||||
return {
|
||||
"extractions": extractions,
|
||||
"total": total_count,
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"total_pages": total_pages,
|
||||
}
|
||||
|
||||
async def get_all_extractions(
|
||||
self,
|
||||
search: str | None = None,
|
||||
sort_by: str = "created_at",
|
||||
sort_order: str = "desc",
|
||||
status_filter: str | None = None,
|
||||
) -> list[ExtractionInfo]:
|
||||
page: int = 1,
|
||||
limit: int = 50,
|
||||
) -> PaginatedExtractionsResponse:
|
||||
"""Get all extractions with filtering, search, and sorting."""
|
||||
extraction_user_tuples = await self.extraction_repo.get_all_extractions_filtered(
|
||||
offset = (page - 1) * limit
|
||||
extraction_user_tuples, total_count = await self.extraction_repo.get_all_extractions_filtered(
|
||||
search=search,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
status_filter=status_filter,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
return [
|
||||
extractions = [
|
||||
{
|
||||
"id": extraction.id
|
||||
or 0, # Should never be None for existing extraction
|
||||
@@ -628,6 +658,16 @@ class ExtractionService:
|
||||
for extraction, user in extraction_user_tuples
|
||||
]
|
||||
|
||||
total_pages = (total_count + limit - 1) // limit # Ceiling division
|
||||
|
||||
return {
|
||||
"extractions": extractions,
|
||||
"total": total_count,
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"total_pages": total_pages,
|
||||
}
|
||||
|
||||
async def get_pending_extractions(self) -> list[ExtractionInfo]:
|
||||
"""Get all pending extractions."""
|
||||
extraction_user_tuples = await self.extraction_repo.get_pending_extractions()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Playlist service for business logic operations."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, TypedDict
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
@@ -14,6 +14,15 @@ from app.repositories.sound import SoundRepository
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class PaginatedPlaylistsResponse(TypedDict):
|
||||
"""Response type for paginated playlists."""
|
||||
playlists: list[dict]
|
||||
total: int
|
||||
page: int
|
||||
limit: int
|
||||
total_pages: int
|
||||
|
||||
|
||||
async def _reload_player_playlist() -> None:
|
||||
"""Reload the player playlist after current playlist changes."""
|
||||
try:
|
||||
@@ -262,6 +271,45 @@ class PlaylistService:
|
||||
current_user_id=current_user_id,
|
||||
)
|
||||
|
||||
async def search_and_sort_playlists_paginated( # noqa: PLR0913
|
||||
self,
|
||||
search_query: str | None = None,
|
||||
sort_by: PlaylistSortField | None = None,
|
||||
sort_order: SortOrder = SortOrder.ASC,
|
||||
user_id: int | None = None,
|
||||
*,
|
||||
include_stats: bool = False,
|
||||
page: int = 1,
|
||||
limit: int = 50,
|
||||
favorites_only: bool = False,
|
||||
current_user_id: int | None = None,
|
||||
) -> PaginatedPlaylistsResponse:
|
||||
"""Search and sort playlists with pagination."""
|
||||
offset = (page - 1) * limit
|
||||
|
||||
playlists, total_count = await self.playlist_repo.search_and_sort(
|
||||
search_query=search_query,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
user_id=user_id,
|
||||
include_stats=include_stats,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
favorites_only=favorites_only,
|
||||
current_user_id=current_user_id,
|
||||
return_count=True,
|
||||
)
|
||||
|
||||
total_pages = (total_count + limit - 1) // limit # Ceiling division
|
||||
|
||||
return PaginatedPlaylistsResponse(
|
||||
playlists=playlists,
|
||||
total=total_count,
|
||||
page=page,
|
||||
limit=limit,
|
||||
total_pages=total_pages,
|
||||
)
|
||||
|
||||
async def get_playlist_sounds(self, playlist_id: int) -> list[Sound]:
|
||||
"""Get all sounds in a playlist."""
|
||||
await self.get_playlist_by_id(playlist_id) # Verify playlist exists
|
||||
|
||||
Reference in New Issue
Block a user