diff --git a/prisma/migrations/20240320231750_modify_video_progress/migration.sql b/prisma/migrations/20240320231750_modify_video_progress/migration.sql deleted file mode 100644 index f9b3e838e..000000000 --- a/prisma/migrations/20240320231750_modify_video_progress/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "VideoProgress" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, -ADD COLUMN "videoDuration" INTEGER NOT NULL DEFAULT 0; diff --git a/prisma/migrations/20240324133414_modify_video_progress/migration.sql b/prisma/migrations/20240324133414_modify_video_progress/migration.sql new file mode 100644 index 000000000..7b93a9771 --- /dev/null +++ b/prisma/migrations/20240324133414_modify_video_progress/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "VideoProgress" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6ccebcfa1..efd19681b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -155,7 +155,6 @@ model VideoProgress { content Content @relation(fields: [contentId], references: [id], onDelete: Cascade) markAsCompleted Boolean @default(false) updatedAt DateTime @default(now()) @updatedAt - videoDuration Int @default(0) @@unique([contentId, userId]) } diff --git a/prisma/seed.ts b/prisma/seed.ts index b26c6078f..14d8490d3 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -45,6 +45,55 @@ async function main() { ], }); + const moreVideos = []; + for (let i = 4; i <= 40; i++) { + moreVideos.push({ + id: i, + type: 'video', + title: `Another test video ${i} for week 1`, + hidden: false, + thumbnail: + 'https://appx-recordings.s3.ap-south-1.amazonaws.com/drm/100x/images/week-1.jpg', + parentId: 1, + commentsCount: 0, + }); + } + + const moreVideosMetadata = []; + for (let i = 2; i <= 38; i++) { + moreVideosMetadata.push({ + id: i, + contentId: i + 2, + video_1080p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + slides: + 'https://appx-recordings.s3.ap-south-1.amazonaws.com/drm/100x/slides/Loops%2C+callbacks.pdf', + duration: 10, + }); + } + await db.content.createMany({ data: [ { @@ -76,6 +125,7 @@ async function main() { parentId: 1, commentsCount: 0, }, + ...moreVideos, ], }); @@ -93,37 +143,81 @@ async function main() { }, }); - await db.videoMetadata.create({ - data: { - id: 1, - contentId: 3, - video_1080p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_1080p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_720p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', - video_360p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', - slides: - 'https://appx-recordings.s3.ap-south-1.amazonaws.com/drm/100x/slides/Loops%2C+callbacks.pdf', - }, + await db.videoMetadata.createMany({ + data: [ + { + id: 1, + contentId: 3, + video_1080p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_1080p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_720p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_mp4_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_1: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_2: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_3: 'https://www.w3schools.com/html/mov_bbb.mp4', + video_360p_4: 'https://www.w3schools.com/html/mov_bbb.mp4', + slides: + 'https://appx-recordings.s3.ap-south-1.amazonaws.com/drm/100x/slides/Loops%2C+callbacks.pdf', + duration: 10, + }, + ...moreVideosMetadata, + ], + }); + + const data = []; + const uniqueDates = [ + ...Array.from({ length: 5 }, () => getRandomDate()), + new Date(Date.now()), + new Date(Date.now() - 24 * 60 * 60 * 1000), + ]; + + for (let i = 3; i <= 40; i++) { + const updatedAt = + uniqueDates[Math.floor(Math.random() * uniqueDates.length)]; + + for (let j = 0; j < 1; j++) { + // Each contentId will have exactly 1 entry + data.push({ + id: i, + contentId: i, + userId: '1', + currentTimestamp: getRandomTimestamp(), + updatedAt, // Set updatedAt time to be the same for the first 5 entries + }); + } + } + + function getRandomTimestamp() { + return Math.floor(Math.random() * 11); // Random number between 0 and 10 + } + + function getRandomDate() { + const startDate = new Date(2023, 0, 1); // Random start date + const endDate = new Date(); // Current date + return new Date( + startDate.getTime() + + Math.random() * (endDate.getTime() - startDate.getTime()), + ); + } + + await db.videoProgress.createMany({ + data, }); } diff --git a/src/app/api/course/videoProgress/route.ts b/src/app/api/course/videoProgress/route.ts index 9e4b435df..15f3ffb14 100644 --- a/src/app/api/course/videoProgress/route.ts +++ b/src/app/api/course/videoProgress/route.ts @@ -27,7 +27,7 @@ export async function GET(req: NextRequest) { } export async function POST(req: NextRequest) { - const { contentId, currentTimestamp, videoDuration } = await req.json(); + const { contentId, currentTimestamp } = await req.json(); const session = await getServerSession(authOptions); if (!session || !session?.user) { return NextResponse.json({}, { status: 401 }); @@ -43,11 +43,9 @@ export async function POST(req: NextRequest) { contentId: Number(contentId), userId: session.user.id, currentTimestamp, - videoDuration, }, update: { currentTimestamp, - ...(videoDuration && { videoDuration }), }, }); revalidatePath('/history'); diff --git a/src/app/history/page.tsx b/src/app/history/page.tsx index db34bc25e..0b30265d5 100644 --- a/src/app/history/page.tsx +++ b/src/app/history/page.tsx @@ -8,6 +8,7 @@ import { Fragment } from 'react'; export type TWatchHistory = VideoProgress & { content: Content & { parent: { id: number; courses: CourseContent[] } | null; + VideoMetadata: { duration: number | null } | null; }; }; @@ -51,6 +52,11 @@ async function getWatchHistory() { include: { content: { include: { + VideoMetadata: { + select: { + duration: true, + }, + }, parent: { select: { id: true, diff --git a/src/components/VideoPlayer2.tsx b/src/components/VideoPlayer2.tsx index 44b351cac..adec5eadb 100644 --- a/src/components/VideoPlayer2.tsx +++ b/src/components/VideoPlayer2.tsx @@ -36,7 +36,6 @@ export const VideoPlayer: FunctionComponent = ({ }) => { const videoRef = useRef(null); const playerRef = useRef(null); - const [videoDuration, setVideoDuration] = useState(null); const [player, setPlayer] = useState(null); const searchParams = useSearchParams(); useEffect(() => { @@ -190,22 +189,6 @@ export const VideoPlayer: FunctionComponent = ({ if (currentTime <= 20) { return; } - const duration = Math.floor(player.duration()); - if (!videoDuration) { - await fetch('/api/course/videoProgress', { - body: JSON.stringify({ - currentTimestamp: currentTime, - contentId, - videoDuration: duration, - }), - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }); - setVideoDuration(duration); - return; - } await fetch('/api/course/videoProgress', { body: JSON.stringify({ currentTimestamp: currentTime, diff --git a/src/components/WatchHistoryClient.tsx b/src/components/WatchHistoryClient.tsx index b4a0b42a6..a1d35a2e6 100644 --- a/src/components/WatchHistoryClient.tsx +++ b/src/components/WatchHistoryClient.tsx @@ -30,18 +30,18 @@ const HistoryCard = ({ id, contentId, currentTimestamp, - videoDuration, - content: { type, title, thumbnail, hidden, parent }, + content: { type, title, thumbnail, hidden, parent, VideoMetadata }, }: TWatchHistory) => { const router = useRouter(); - if (parent && !hidden && type === 'video') { + if (parent && !hidden && type === 'video' && VideoMetadata) { + const { duration: videoDuration } = VideoMetadata; const { id: folderId, courses } = parent; const courseId = courses[0].courseId; const videoUrl = `/courses/${courseId}/${folderId}/${contentId}`; - const videoProgressPercent = Math.round( - (currentTimestamp / videoDuration) * 100, - ); + const videoProgressPercent = videoDuration + ? Math.round((currentTimestamp / videoDuration) * 100) + : 0; return ( { markAsCompleted: videoProgress.find( (x) => x.contentId === content.id, )?.markAsCompleted, - videoFullDuration: videoProgress.find( - (x) => x.contentId === content.id, - )?.videoDuration, + videoFullDuration: content.VideoMetadata?.duration, } : null, },