Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ec10-10 replace mux library to react player #40

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions components/CourseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const CourseCard = ({ course, isAdmin }: Props) => {
{course.lessons[0]?.video?.publicPlaybackId && (
<Image
className="w-full"
src={`https://image.mux.com/${course.lessons[0]?.video?.publicPlaybackId}/thumbnail.jpg?width=640`}
alt={`Video thumbnail preview for ${course.lessons[0]?.video?.publicPlaybackId}`}
src={`https://img.youtube.com/vi/${course.lessons[0]?.video?.publicPlaybackId}/0.jpg`}
alt={`Video thumbnail preview for ${course.lessons[0]?.name}`}
width={320}
height={240}
/>
Expand Down
2 changes: 1 addition & 1 deletion components/CourseOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const CourseOverview = ({ course }: Props) => {
<div key={lesson.id} className='flex flex-col md:flex-row gap-6 mb-8'>
{lesson.video?.publicPlaybackId && (
<Image
src={`https://image.mux.com/${lesson.video.publicPlaybackId}/thumbnail.jpg?width=640`}
src={`https://img.youtube.com/vi/${lesson.video.publicPlaybackId}/0.jpg`}
alt={`Video thumbnail preview for ${lesson.name}`}
width={320}
height={240}
Expand Down
20 changes: 7 additions & 13 deletions components/CourseViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Image from 'next/future/image'
import type { Course, Lesson, Video } from "@prisma/client"
import Heading from 'components/Heading'
import EmptyState from 'components/EmptyState'
import MuxPlayer from "@mux/mux-player-react/lazy";
import VideoPlayer from 'components/VideoPlayer'
import formatDuration from 'utils/formatDuration'
import clsx from 'clsx';
import type { UserLessonProgress } from '@prisma/client'
Expand All @@ -27,7 +27,6 @@ const CourseViewer = ({ course, lessonProgress = [], setLessonProgress }: Props)
const [activeLesson, setActiveLesson] = useState(course.lessons[lessonIndex]);
const playbackId = activeLesson?.video?.publicPlaybackId
const videoReady = activeLesson?.video?.status === "ready"
const placeholder = activeLesson?.video?.placeholder

useEffect(() => {
const lessonIndex = course.lessons.findIndex(lesson => lesson.id === activeLesson.id) + 1
Expand Down Expand Up @@ -60,17 +59,12 @@ const CourseViewer = ({ course, lessonProgress = [], setLessonProgress }: Props)
<div className='px-5 grid lg:grid-cols-[70%_30%]'>
<div>
{playbackId && videoReady ? (
<MuxPlayer
className='mb-6 w-full aspect-video'
streamType="on-demand"
<VideoPlayer
playbackId={playbackId}
placeholder={placeholder}
onEnded={markLessonCompleted}
metadata={{
video_series: activeLesson.courseId,
video_title: activeLesson.name,
player_name: "Video Course Starter Kit",
}}
courseId={activeLesson.courseId}
lessonName={activeLesson.name}
videoUrl={`https://www.youtube.com/watch?v=${playbackId}`}
thumbnail={`https://img.youtube.com/vi/vi/${playbackId}/0.jpg`}
/>
) : (
<div className='mb-6 w-full aspect-video bg-gray-200' />
Expand Down Expand Up @@ -98,7 +92,7 @@ const CourseViewer = ({ course, lessonProgress = [], setLessonProgress }: Props)

{lesson.video?.publicPlaybackId && lesson.video.status === "ready" && (
<Image
src={`https://image.mux.com/${lesson.video.publicPlaybackId}/thumbnail.jpg?width=640`}
src={`https://img.youtube.com/vi/${lesson.video.privatePlaybackId}/0.jpg`}
alt={`Video thumbnail preview for ${lesson.name}`}
width={106}
height={60}
Expand Down
34 changes: 34 additions & 0 deletions components/VideoPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import ReactPlayer from 'react-player';

interface VideoPlayerProps {
playbackId: string;
courseId: number;
lessonName: string;
videoUrl: string;
thumbnail?: string;
}

const VideoPlayer: React.FC<VideoPlayerProps> = ({playbackId, courseId, lessonName, videoUrl, thumbnail}) => {
const handleMetadata = (): void => {
console.log({
video_series: courseId,
video_title: lessonName,
player_name: "Video Course Starter Kit",
});
};

return (
<div className='mb-6 w-full aspect-video'>
<ReactPlayer
url={videoUrl}
className='react-player'
width='100%'
height='100%'
controls={true}
onReady={handleMetadata}
/>
</div>
);
};

export default VideoPlayer;
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images: {
domains: ['image.mux.com'],
domains: ['img.youtube.com'],
},
}

Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
"db:proxy": "pscale connect video-course-starter-kit main --port 3309"
},
"dependencies": {
"@mux/blurhash": "^0.1.2",
"@mux/mux-node": "^6.3.0",
"@mux/mux-player-react": "^1",
"@mux/mux-uploader-react": "^1.0.0-beta.0",
"react-player": "^2.14.0",
"@next-auth/prisma-adapter": "^1.0.4",
"@planetscale/database": "^1.3.0",
"@prisma/client": "4.3.1",
Expand Down
2 changes: 1 addition & 1 deletion pages/admin/courses/[courseId]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const AdminCourseEdit: NextPage<AdminCourseEditPageProps> = ({ course }) => {
<a className='flex gap-4 border border-gray-200 rounded-lg mb-6 cursor-pointer'>
{lesson.video?.publicPlaybackId && (
<Image
src={`https://image.mux.com/${lesson.video.publicPlaybackId}/thumbnail.jpg?width=640`}
src={`https://img.youtube.com/vi/${lesson.video.publicPlaybackId}/0.jpg`}
alt={`Video thumbnail preview for ${lesson.name}`}
width={180}
height={100}
Expand Down
15 changes: 6 additions & 9 deletions pages/admin/courses/[courseId]/lessons/[lessonId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Session } from 'next-auth'
import type { Lesson, Video } from '@prisma/client'
import { useRouter } from 'next/router'
import { SubmitHandler } from "react-hook-form";
import MuxPlayer from "@mux/mux-player-react/lazy";
import VideoPlayer from 'components/VideoPlayer';
import LessonForm, { Inputs } from 'components/forms/LessonForm'
import Button from 'components/Button'
import toast from 'react-hot-toast';
Expand Down Expand Up @@ -64,15 +64,12 @@ const AdminLessonEdit: NextPage<AdminLessonEditPageProps> = ({ lesson }) => {
<div className='grid lg:grid-cols-2 gap-6'>
<div>
{lesson.video?.status === "ready" && lesson.video.publicPlaybackId ? (
<MuxPlayer
className='mb-6 w-full aspect-video'
streamType="on-demand"
<VideoPlayer
playbackId={lesson.video.publicPlaybackId}
metadata={{
video_series: lesson.courseId,
video_title: lesson.name,
player_name: "Video Course Starter Kit",
}}
courseId={lesson.courseId}
lessonName={lesson.name}
videoUrl={lesson.video.publicPlaybackId}
thumbnail={`https://img.youtube.com/vi/${lesson.video.publicPlaybackId}/0.jpg`}
/>
) : (
<div className='mb-6 w-full aspect-video bg-gray-200' />
Expand Down
38 changes: 2 additions & 36 deletions pages/admin/courses/[courseId]/lessons/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { useForm, SubmitHandler, FormProvider } from "react-hook-form";
import { useRouter } from 'next/router'
import { prisma } from 'utils/prisma'

import Mux from '@mux/mux-node';
const { Video } = new Mux(process.env.MUX_TOKEN_ID, process.env.MUX_TOKEN_SECRET);

import MuxUploader from '@mux/mux-uploader-react';
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from 'pages/api/auth/[...nextauth]'
import type { Session } from 'next-auth'
Expand All @@ -17,30 +13,24 @@ import toast from 'react-hot-toast';
import Heading from 'components/Heading';
import TextInput from 'components/forms/TextInput';
import TextAreaInput from 'components/forms/TextAreaInput';
import Field from 'components/forms/Field';
import SubmitInput from 'components/forms/SubmitInput';

type Inputs = {
name: string;
description: string;
uploadId: string;
courseId: string;
};

type AdminNewLessonPageProps = {
session: Session;
uploadUrl: string;
uploadId: string;
}

type LessonCreateResult = {
id: number;
}

const AdminNewLesson: NextPage<AdminNewLessonPageProps> = ({ uploadUrl, uploadId }) => {
const AdminNewLesson: NextPage<AdminNewLessonPageProps> = ({}) => {
const router = useRouter()
const courseId = router.query.courseId as string
const [isVideoUploaded, setIsVideoUploaded] = useState(false)

const methods = useForm<Inputs>();

Expand Down Expand Up @@ -72,25 +62,12 @@ const AdminNewLesson: NextPage<AdminNewLessonPageProps> = ({ uploadUrl, uploadId
<TextInput label='Name' name='name' options={{ required: true }} />
<TextAreaInput label='Description' name='description' options={{ required: true }} />

<Field>
<MuxUploader
endpoint={uploadUrl}
type="bar"
status
style={{ '--button-border-radius': '40px' }}
onSuccess={() => setIsVideoUploaded(true)}
className='w-full mb-6'
/>
</Field>

<input type="hidden" {...methods.register("uploadId", { value: uploadId, required: true })} />
<input type="hidden" {...methods.register("courseId", { value: courseId, required: true })} />

<input
type="submit"
className='bg-blue-500 text-white p-4 disabled:bg-slate-50 disabled:text-gray-400 cursor-pointer disabled:cursor-not-allowed w-fit'
value='Create lesson'
disabled={!isVideoUploaded}
/>
</form>
</FormProvider>
Expand All @@ -112,17 +89,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
}
}

const upload = await Video.Uploads.create({
cors_origin: 'https://localhost:3000',
new_asset_settings: {
playback_policy: ['public', 'signed'],
passthrough: JSON.stringify({ userId: session.user?.id })
}
});

await prisma.video.create({
data: {
uploadId: upload.id,
owner: {
connect: { id: session.user.id }
}
Expand All @@ -131,9 +99,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {

return {
props: {
session,
uploadId: upload.id,
uploadUrl: upload.url
session
},
}
}
8 changes: 1 addition & 7 deletions pages/api/lessons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default async function assetHandler(req: NextApiRequest, res: NextApiResp

switch (method) {
case 'POST':
const { name, description, courseId, uploadId } = JSON.parse(req.body)
const { name, description, courseId } = JSON.parse(req.body)

try {
const id = session?.user?.id
Expand All @@ -37,7 +37,6 @@ export default async function assetHandler(req: NextApiRequest, res: NextApiResp

const [video] = await prisma.video.findMany({
where: {
uploadId,
owner: {
id: {
equals: id
Expand All @@ -59,11 +58,6 @@ export default async function assetHandler(req: NextApiRequest, res: NextApiResp
connect: {
id: course.id
}
},
video: {
connect: {
uploadId
}
}
}
})
Expand Down
3 changes: 0 additions & 3 deletions pages/api/webhooks/mux.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next'
import type { Readable } from 'node:stream';

import Mux from '@mux/mux-node';

import WEBHOOK_TYPES from "../../../utils/webhooks/mux/types"
import get from "lodash.get"

Expand All @@ -25,7 +23,6 @@ async function buffer(readable: Readable) {
function verifyWebhookSignature(rawBody: string | Buffer, req: NextApiRequest) {
if (webhookSecret) {
// this will raise an error if signature is not valid
Mux.Webhooks.verifyHeader(rawBody, req.headers['mux-signature'] as string, webhookSecret);
} else {
console.log('Skipping webhook signature verification because no secret is configured'); // eslint-disable-line no-console
}
Expand Down
3 changes: 0 additions & 3 deletions pages/courses/[...slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { ReactElement } from 'react'
import { useState } from 'react'
import type { GetServerSideProps } from 'next'
import type { Course, Lesson, Video } from "@prisma/client"
import muxBlurHash from "@mux/blurhash";
import { prisma } from 'utils/prisma'
import { authOptions } from 'pages/api/auth/[...nextauth]'
import { unstable_getServerSession } from "next-auth/next"
Expand Down Expand Up @@ -91,8 +90,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => {

course.lessons = await Promise.all(course.lessons.map(async (lesson) => {
if (lesson?.video?.publicPlaybackId) {
const { blurHashBase64 } = await muxBlurHash(lesson.video.publicPlaybackId);
(lesson.video as VideoWithPlaceholder).placeholder = blurHashBase64;
}
return lesson
}))
Expand Down
Loading