feat: add video watch progress tracking with resume functionality

Implements a complete watch progress system that allows users to:
- Save playback position automatically every 5 seconds while watching
- Resume from last position when returning to a video
- Save progress on page leave using sendBeacon for reliability

Also includes:
- Optimized slug generation in projects and workspaces APIs (single query vs loop)
- Added isActive filter to version queries across all video endpoints
- Added pagination support for video and comment queries
- Enhanced database pool management with connection limits and graceful shutdown
- New WatchProgress Prisma model with user-version relations
This commit is contained in:
Yusuf İpek
2026-02-14 15:25:32 +03:00
parent 2888f7de98
commit 88c74d646e
10 changed files with 484 additions and 38 deletions
@@ -15,25 +15,37 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
const session = await auth();
const { projectId, videoId } = await params;
// Parse query params for pagination and options
const searchParams = request.nextUrl.searchParams;
const commentLimit = Math.min(parseInt(searchParams.get('commentLimit') || '50'), 100);
const commentOffset = parseInt(searchParams.get('commentOffset') || '0');
const includeReplies = searchParams.get('includeReplies') === 'true';
const video = await db.video.findFirst({
where: { id: videoId, projectId },
include: {
project: true,
versions: {
where: { isActive: true },
orderBy: { versionNumber: 'desc' },
take: 1,
include: {
comments: {
orderBy: { timestamp: 'asc' },
skip: commentOffset,
take: commentLimit,
include: {
author: { select: { id: true, name: true, image: true } },
tag: { select: { id: true, name: true, color: true } },
replies: {
orderBy: { createdAt: 'asc' },
include: {
author: { select: { id: true, name: true, image: true } },
tag: { select: { id: true, name: true, color: true } },
...(includeReplies ? {
replies: {
orderBy: { createdAt: 'asc' },
include: {
author: { select: { id: true, name: true, image: true } },
tag: { select: { id: true, name: true, color: true } },
},
},
},
} : {}),
},
where: { parentId: null }, // Only top-level comments
},