
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
👉 Bunny (create your free account and get one extra free month): https://jsm.dev/jsm-bunny
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, the speaker discusses how to set up a robust infrastructure for storing and distributing videos using Bunny CDN. The focus is on the step-by-step process of creating storage zones, configuring settings, and implementing the necessary API calls to upload videos and related metadata. This is a crucial part of managing educational content effectively and ensuring global accessibility.
00:00:02 And finally, the moment you've been waiting for.
00:00:05 Now that we have the upload a video form, we need an infrastructure that'll allow us to store and distribute those videos all around the world.
00:00:15 And for that, my number one choice is Bunny.
00:00:18 I've been using Bunny internally for years now to host every single one of our educational videos on JS Mastery Pro.
00:00:25 And that's a pretty big deal, because we have to ensure that every single video is working at all times and that people can access it from anywhere around
00:00:34 the world.
00:00:35 So, I'm super excited that I got a chance to teach you how to implement the best video CDN in the world.
00:00:41 The link down in the description will double your free access to about a month.
00:00:46 No credit card is required.
00:00:47 You can get started in two minutes.
00:00:49 And then, even if it expires, your monthly minimum is $1. And for that, you can host and share a bunch of different videos.
00:00:58 So, click the link down in the description, and create your account.
00:01:01 Once you're in, on the left sidebar, head over to Storage, and create your first storage zone.
00:01:07 Give it a name, start with something like JSM, and then you can say Snapcast, or feel free to choose your own name.
00:01:15 Leave all the other fields as they are, except the georeplication.
00:01:19 In the image below, you can see multiple locations checked initially.
00:01:22 But for now, even just this single location will be enough, so I'll choose the one that is closest to me.
00:01:28 This leaves our monthly storage cost at 1 cent per gigabyte.
00:01:32 Oh, and I can notice that Bunny recommends having at least the one replication zone enabled.
00:01:36 So I'll go ahead and turn on one in North America as well.
00:01:40 So people from there get faster access to our videos, which increases our storage by one cent per gigabyte.
00:01:46 Perfect.
00:01:47 Now create a new folder called thumbnails.
00:01:53 Go to the FTP and API access and copy this password from here.
00:01:58 Then head over to your .env and paste it at the bottom.
00:02:04 I'll say bunny, and here I'll save it as bunny storage access key, and make it equal to the password we just copied.
00:02:15 After that, we'll also need to use this host name.
00:02:18 So I'll collapse this, and we won't need to put it within ENVs, because it doesn't necessarily have to be for your eyes only,
00:02:25 but it's gonna be good to have it stored somewhere so we can reuse it later on.
00:02:29 So I'll head over into Constants.
00:02:32 That is right here.
00:02:34 And you can notice that there are already some values that I provided for you before from my previous instance of Bunny,
00:02:40 but now we'll have to update all of those.
00:02:43 Starting with the storage base URL.
00:02:45 You want to make it start with https colon forward slash forward slash.
00:02:50 Then you can copy the host name.
00:02:52 It is possible that for you, your host name also includes some kind of a prefix.
00:02:57 That's totally okay.
00:02:58 And then you also have to add a forward slash and then paste your username.
00:03:04 So the storage base URL is combined out of the hostname forward slash username.
00:03:10 So you'll definitely have to update this.
00:03:12 Now, after we store our files, we also need to create something known as a pool zone.
00:03:18 That pool zone will allow us to get our thumbnail URL.
00:03:22 So head over to CDN and create a new pool zone.
00:03:27 You can give it a name like jsm-snapcast, and then for the origin type, select storage zone, and then select your storage bucket.
00:03:37 You can choose however many zones you'd like your data to be served from.
00:03:41 This is super useful if your users are globally distributed.
00:03:44 But in this case, I'll proceed just with Europe and North America.
00:03:49 And I'll select Add Pool Zone.
00:03:52 Then copy this host name and add it to your constants under CDN URL.
00:03:58 You have to leave https:// and then add the hostname, which you can copy from here.
00:04:06 This goes under the CDN URL.
00:04:08 Now, after we store the videos, we also need to be able to stream them to the users.
00:04:13 So, head over to Stream on the left sidebar and create our first video library.
00:04:18 Once again, you can follow all the same steps.
00:04:21 I'll call it the same thing once again.
00:04:24 And I'll choose two zones from georeplication and click add video library.
00:04:30 Now head over into the API section on the left side and copy the video library ID and add it over to your ENVs.
00:04:39 Right here under bunny, you can call it bunny library ID.
00:04:45 And you'll also have to do the same thing for the stream access key.
00:04:49 So copy the API key and paste it right here as bunny stream access key.
00:04:57 Perfect.
00:04:58 Also back within the constants, there's this transcript URL, and this will be equal to https colon forward slash forward slash.
00:05:07 And then you have to add your CDN host name.
00:05:09 So you can copy it right from here.
00:05:13 Perfect.
00:05:14 And finally, the stream base URL and the embed URL will remain the same as they were within the constants.
00:05:22 You only needed to change these three.
00:05:25 But what is this transcript thing?
00:05:26 Well, right here under stream, if you head over to transcribing, you can enable transcribing.
00:05:33 And that means that all videos will automatically be transcribed.
00:05:37 This transcription can be used to automatically convert the audio track into captions translated into multiple languages.
00:05:44 So I'll definitely turn this on.
00:05:47 And if you want to, you can also turn on the Smart Title and Smart Description, which will automatically update the metadata of the video based on its contents.
00:05:56 And here you can choose the languages you want to translate it to.
00:06:00 On JS Mastery Pro, I decided to transcribe the videos into four of our most popular languages.
00:06:06 English, Hindi, and Spanish.
00:06:09 So now that we have created the necessary storage and stream zones, we're ready to upload our first video.
00:06:16 And technically, you could do it through Bunny's GUI right here on their platform.
00:06:21 You just click upload a video and do it right here.
00:06:24 But that's not what we want to do.
00:06:26 We want to put our form to use.
00:06:28 So we want to upload the video here and store it and distribute it through Bunny.
00:06:32 To do that, we'll first create some helper functions within lib, and a new file called utils.ts.
00:06:41 These utility functions will make it easier for us to upload those videos and to create additional functionalities.
00:06:48 So I'll include a couple of these utils right here within the video kit.
00:06:52 So you can copy them and paste them right here.
00:06:56 Let me show you what we have here.
00:06:58 We have a couple of functions, so let me collapse them all.
00:07:03 We have one called getENV, which simply tries to access the ENV, and if it doesn't exist, it throws an error, but if it does,
00:07:11 it returns the actual value.
00:07:13 The second one is a helper function that allows us to more easily make calls to Bunny.
00:07:18 You can just choose whether you want to head over for streaming the videos or storing them, pass the necessary headers, the request options as well,
00:07:28 And finally, upload the video and return the uploaded data.
00:07:32 And finally, we have a higher order function that allows us to handle errors.
00:07:37 What does this mean?
00:07:38 We'll wrap our other API calls with the error handling function.
00:07:43 And that way we don't have to repeat the try and catch block every time.
00:07:47 Rather, we'll always be able to catch the errors in a consistent way.
00:07:51 Again, these are just a couple of functions that will make it easier for us to focus on what matters.
00:07:57 And that is getting our videos uploaded.
00:07:58 I also noticed that we haven't added this CN function.
00:08:02 This is for tailwind, so we can add dynamic styles at the top of our utils file.
00:08:07 But for that, we'll have to install two packages.
00:08:10 So open up your terminal and say, mpm install clsx and tailwind merge.
00:08:18 And I just noticed that later on, I'll also provide you with a couple more utility functions.
00:08:22 So instead of having them all separated like this, I'll just merge all of these into a single file so you can copy it right off the bat.
00:08:29 That should look something like this.
00:08:30 You'll get functions that allow us to more easily parse the transcript or figure out how many days ago this video was posted.
00:08:37 Then we have a couple of others that you have already seen for getting the ENVs and making API calls more easily.
00:08:44 For the time being, we don't yet have the schema for the videos, so I'll simply comment it out.
00:08:49 And I'll also comment out the use of this function that actually calls these videos.
00:08:55 We'll implement this soon.
00:08:57 But for the time being, now we are finally ready to create our first server action that will allow the video upload.
00:09:06 So to do that, let's create a new folder within the lib folder and let's call it actions.
00:09:13 And within actions, create a new file and call it video.ts.
00:09:19 So here we'll have all the video related actions.
00:09:22 Since this file will be dedicated only to the server actions, we have to make sure that Next.js only calls it in the server.
00:09:29 So this file has to be secure and it must not be called from the client side.
00:09:34 So to do that, we have to give it a use server directive at the top.
00:09:38 And then we are ready to export and create our first server action, which we can call getVideoUploadURL.
00:09:48 This will upload the video and give us back the URL.
00:09:51 So we could create just a regular function like this.
00:09:55 And most of these are going to be asynchronous.
00:09:57 But what I want to do first is wrap it with error handling.
00:10:02 This is one of our utility functions that I showed you that'll simplify the error handling for us and make it more consistent.
00:10:09 So the only thing you have to do is say with error handling and then wrap this function call.
00:10:16 If you do this, now automatically for every single one of these calls, it'll try to open up a try and catch block.
00:10:23 If there's a result, it'll simply return it, but if there's an error message, it'll then return that error message.
00:10:30 That way, we don't have to say, try this, catch that, and so on for every single one of these server actions.
00:10:37 Now, what matters for us uploading the video is to know which user is uploading it.
00:10:42 And for that, we need to get access to the current user from the session.
00:10:46 So for that, I'll create a helper function.
00:10:50 So I'll say helper functions right here at the top, not a server action, and then we have server actions right below.
00:10:58 So the first helper function will be called getSessionUserID.
00:11:05 It'll be equal to an asynchronous function that returns a promise, which will resolve in a string, which is basically just the ID of the user.
00:11:15 And the way it works is that first we'll get access to the session by calling await auth, coming from auth.ts.api.getSession.
00:11:28 And to it, we have to pass the headers of await headers, And these headers are coming from next headers.
00:11:36 So now, if we don't have the session, so if there is no session, we want to exit out of the function and say throw new error,
00:11:45 which can say unauthenticated.
00:11:47 But if we do have a session, We'll return that current session user ID.
00:11:55 So this is our helper function, which now we can use within every single server action that requires our user to be logged in.
00:12:03 So I'll say const get session user ID.
00:12:09 Like this.
00:12:10 In this case, we don't have to extract anything from it.
00:12:13 So I'm just going to await it.
00:12:14 Later on, if we do need to get the ID, you can just get it like this.
00:12:18 User ID is equal to await getSessionUserID.
00:12:21 And now we are ready to make our first API call to Bunny.
00:12:25 So I'll say await.
00:12:27 API fetch.
00:12:29 This is our helper function that allows us to more easily fetch by simply providing a set of URL and options to it.
00:12:36 Think of this as just an abstraction for doing a regular fetch call, okay?
00:12:41 So I can say API fetch and the first thing we have to pass into it is the URL that we want to make a request to.
00:12:48 So this will be a template string of, and now we have to get access to some of those keys coming from environment variables or from constants.
00:12:58 So we can get all of those important keys right here.
00:13:01 by saying const videoStreamBaseUrl is equal to bunny, and this bunny is coming from constants.streamBaseUrl.
00:13:15 So now we can see from constants, we're exporting this bunny object, and now we can just import it right here.
00:13:21 For the second one, we'll need to get access to the thumbnail storage base URL, and this one is also coming from bunny.storage base URL.
00:13:34 After that, we have the thumbnail CDN URL equal to bunny.cdn URL.
00:13:42 We'll also need to get access to the bunny library ID.
00:13:45 So I'll say const bunny library ID.
00:13:51 Is equal to get ENV.
00:13:53 So we're using that helper function and we have to pass the name of the environment variable bunny library ID.
00:14:02 And we can do the same thing with the axis keys.
00:14:05 So I'll say const axisKeys is equal to an object where the streamAxisKey is equal to getEnv of bunnyStreamAxisKey.
00:14:21 And the second one is the storageAxisKey, which is equal to getEnv and key will be bunnyStorage.
00:14:34 axis key.
00:14:36 Make sure that it's all underscores.
00:14:38 So I wanted to do this to fetch both the ENVs as well as variables from constants right into this file, so now we can use them to form our API endpoints.
00:14:48 So we have to start with the base URL of the stream.
00:14:51 So I'll say video, stream, base URL, forward slash, bunny library ID to get to our library, forward slash videos, so that we know that we're uploading
00:15:04 the videos.
00:15:05 Next, we can provide an object of options.
00:15:09 I'll say that the method is post.
00:15:12 The bunny type will be set to stream, and we can pass the body of the video we're trying to upload.
00:15:18 For now, as the title, I'll set the temporary title.
00:15:22 And for the collection ID, I will leave it empty.
00:15:26 Now, once we make this request to Bunny, it'll give us back the video response.
00:15:31 So I'll say const video response is equal to this call we're making.
00:15:37 And then from that video response, we can get access to the upload URL.
00:15:41 So I'll say const upload URL is equal to, once again, I'll do a template string of video stream base URL forward slash bunny library ID forward slash videos
00:15:57 forward slash video response dot GUID.
00:16:01 And now that we have uploaded the video, we can just return the most important information back to the client.
00:16:08 So that's going to be the video ID equal to video response dot GUID upload URL.
00:16:19 as well as the access key equal to access keys dot stream access key.
00:16:26 Perfect.
00:16:27 And now we'll have to do a very similar thing to upload our thumbnail.
00:16:32 So I'll collapse the server action and I'll create a new one.
00:16:36 Export const getThumbnailUploadURL.
00:16:43 I'll once again wrap it with error handling and make it an asynchronous function.
00:16:49 That accepts the video ID that we want to attach that thumbnail to.
00:16:54 as its first and only parameter.
00:16:56 Within it, we first want to get access to the file name.
00:17:00 So I'll say const, file name is equal to.
00:17:05 We want to make it unique in case we have multiple uploads.
00:17:08 So I'll use a template string of date.now, and then I'll also apply a video ID that we want to add it to, and then we can say dash thumbnail.
00:17:20 Once we have the file name, we are ready to upload it.
00:17:23 By saying const upload URL is equal to, once again, we want to form the URL.
00:17:29 So that's going to be thumbnail, storage base URL, forward slash thumbnails, forward slash, and then the file name.
00:17:38 And then we want to get access to the CDN URL of that file by saying const.
00:17:44 Now that we have all of that info, we can just return it to the client by saying return upload URL, CDN URL, and finally the access key,
00:18:07 this time pointing to the storage.
00:18:09 So I'll say access keys dot storage access key.
00:18:15 And that gives us our second server action, this time to upload the thumbnail and attach it to a video.
00:18:22 But now, keep this in mind.
00:18:24 We'll upload this video to Bunny and sure, it'll be saved within Bunny storage and you'll be able to see it within their Graphical User Interface.
00:18:32 But we also have to attach some metadata to it and add it to our own database, which means that we'll need another function,
00:18:41 which we can call saveVideoDetails.
00:18:44 So I'll say export const saveVideoDetails is equal to withErrorHandling.
00:18:52 We accept the video details from the form as a param of a type of video details.
00:18:59 But of course, this is within a new asynchronous function.
00:19:02 So let's do it like this.
00:19:03 And these video details will include the video ID, title, description, everything that we have within our form.
00:19:10 And we'll also use this function to attach the user that created the video with the video details itself.
00:19:17 So we have to get access to the user ID by saying const user ID.
00:19:22 is equal to await get session user ID.
00:19:28 And now we want to use these video details to update the title and the description of the video.
00:19:34 So I'll say await API fetch to video stream base URL forward slash bunny library ID forward slash videos.
00:19:48 forward slash video details dot video ID So we want to update this specific video by providing additional options of method equal to post bunny type equal
00:20:03 to stream and then most importantly the body which has the title equal to VideoDetails.title, coming from the form on our front end,
00:20:15 and the description equal to VideoDetails.description.
00:20:20 So we're updating it within Bunny as well.
00:20:22 After that, we want to finally insert it within our database.
00:20:27 So I'll say await db, and this database is coming from db.ts.insert, videos, and then we want to apply specific values to those videos.
00:20:41 But hey, what are these videos?
00:20:43 Where are the videos coming from?
00:20:45 Well, videos will be a new schema within Drizzle.
00:20:49 So head over to Drizzle, schema.ts, and here where we have the user session account and verification, which are all different schemas regarding authentication,
00:21:00 we can now add a new one for videos.
00:21:03 So I'll say export const videos is equal to PG table, of videos, and we can provide the schema.
00:21:13 Now this is a longer one, so I don't think we have to type it by hand.
00:21:16 Rather, I'll leave it for you right here within the video kit.
00:21:19 So you can just copy it and paste it right here.
00:21:24 You can see it has quite a few fields, but don't worry because we're going to go through them together.
00:21:29 Make sure to import the UUID coming from DrizzleORM PG Core.
00:21:36 And in this case, we won't need the reactions.
00:21:38 It's possible they're already deleted for you, but I'm going to delete them in any case.
00:21:43 So what do we have here?
00:21:45 We have the ID, which acts as the primary key of every video, a title, a description, video URL, video ID, thumbnail URL,
00:21:55 visibility, which can be either public or private, a user ID that uploaded that video, views on the video, the duration of the video,
00:22:05 and finally created ad and updated ad fields.
00:22:09 Once you have added this to your schema, you have to push it to the database.
00:22:14 And you can do that by opening your terminal and running mpx drizzle-kit and then push and press enter.
00:22:25 It's going to pull the current schema from the database and apply changes based on this new schema that we added.
00:22:31 So now if you head back over to the actions file where we need those videos, it's going to be very easy to import those videos coming from schema right here.
00:22:42 And now we can attach additional values to it, such as spread all the video details, define the video URL, which is going to be equal to a template string
00:22:54 of bunny We need to attach this video to a user that created it, or the user ID.
00:23:11 And we can set the created ad equal to new date, as well as updated ad, which can be set to new date.
00:23:21 Perfect.
00:23:23 Once we do this in Next.js, you have to revalidate the paths.
00:23:28 This means that you're telling Next.js to reload your page on that specific domain to be able to fetch the new details.
00:23:35 Typically, you can re-validate only one path at a time, but in this case, sometimes we'll have to re-validate a few more paths,
00:23:43 so what we can do is create another helper function called re-validatePaths.
00:23:50 which accepts all the paths, which is an array of strings, and it simply maps over them, paths.forEach, and for each path,
00:24:01 we call the revalidate path, coming from next cache, and then to it we pass the path we want to revalidate.
00:24:09 So now, back in here, we can call revalidate paths and pass an array of just a forward slash because for now, we just want to revalidate the homepage after
00:24:20 we insert a new video.
00:24:21 Also, we can return this newly created video ID by getting it from videoDetails.videoId.
00:24:31 Now, we're not only uploading the video and the thumbnail, but we're also inserting these new videos, thumbnails, and the video details to our database.
00:24:41 So we're one step closer to actually testing it out, and we can do it by modifying our upload page.
00:24:49 So head over to upload page, and then right here within the handle submit function, let's handle the video upload.
00:24:57 First things first, we have to get access to the upload URL that'll then allow us to upload the video.
00:25:03 Thankfully, we have created a server action that helps us with just that.
00:25:07 So we can call this API by saying await get video upload URL.
00:25:15 And to it, we don't have to pass anything.
00:25:18 This will just give us the video upload URL for our specific library.
00:25:23 This function though, will return three things.
00:25:26 The video ID, the upload URL, which we can rename to video upload URL, because later on we'll have the same thing for the thumbnail.
00:25:34 and then access key equal to video access key.
00:25:39 Now, let's check whether we successfully got those values back by saying if there is no video upload URL or if there is no video access key.
00:25:48 In that case, simply throw a new error saying failed to get video upload credentials.
00:25:58 Perfect.
00:25:59 But if we have access to them, we are finally ready to upload the video to Bunny.
00:26:05 So the first part, or basically step zero, was getting the upload URL.
00:26:10 So get upload URL.
00:26:12 And now step one is to actually upload the video.
00:26:16 And to do that, let's create one simple function right here at the top of this file, which I'll call upload file to bunny.
00:26:26 And I want to extract it because a file can be both a thumbnail as well as a video.
00:26:31 It'll accept the file itself of a type file.
00:26:34 the upload URL that we're uploading to of a type string and the access key of a type string as well.
00:26:42 And it'll return a promise that doesn't have to be resolved.
00:26:46 Within here, we can just return the output of a fetch call.
00:26:51 A fetch call to the upload URL that we pass into the function with the following options.
00:26:58 method of put because we're trying to upload a file headers equal to content type.
00:27:08 of file type and access key of access key so we're now just sharing all the necessary keys in order to be able to upload the file which we're going to
00:27:21 do through the body so body will be set to the file we can then call a dot then to get the response and then if a response is not okay we can simply throw
00:27:34 a new error by saying upload failed and that's it now we have this function that allows us to upload the video to the upload url that we have received
00:27:44 from this server action so we can now call it by saying await upload file to bunny in this case we want to upload the video file to the video upload URL,
00:27:59 and we need to pass the video access key.
00:28:02 Now we want to repeat the same exact thing with the thumbnail upload.
00:28:07 So I'll copy this part where we're getting the video upload URL and I'll paste it here, but I'll change it to get thumbnail upload URL.
00:28:18 From it, we get the upload URL, which we can rename to thumbnail upload URL and thumbnail access key, and for the thumbnails,
00:28:29 we also have a CDN URL, which is going to be thumbnail CDN URL.
00:28:37 So now we can do the same thing that we did before.
00:28:39 We can check whether these exist, but don't forget to pass the video ID into it because this one needs to attach the thumbnail to a video ID.
00:28:47 And this video ID is coming right from above right here.
00:28:51 Then we can do the checks, same as here.
00:28:54 If there is no.
00:28:56 thumbnail upload URL, or if there's no thumbnail CDN URL, or if there is no thumbnail access key.
00:29:08 In that case, we want to say throw new error, fail to get thumbnail upload credentials.
00:29:16 But if we finally get it, we want to upload the thumbnail to Bunny by saying await, upload file to Bunny, we can pass the thumbnail.file,
00:29:26 the thumbnail upload URL, as well as the thumbnail access key.
00:29:32 And when we pair it all together, we are ready to save the final video details within our database by calling our server action called saveVideoDetails
00:29:44 To which we can pass the video ID, the thumbnail URL of a thumbnail CDN URL, because we're hosting it on a content delivery network.
00:29:54 We want to spread the form data because it actually contains the title, description, visibility, and so on.
00:30:01 And duration can be set to video duration.
00:30:05 This video duration has to come from the state.
00:30:08 So I'll create a new state for the duration at the top by saying use state snippet.
00:30:16 I'll call it video duration and set video duration at the start set to zero.
00:30:22 And then I'll create a new use effect so that whenever the video duration changes, and we can control that through the dependency array.
00:30:32 So video.duration, whenever it changes, we want to check if the video.duration is not equal to null.
00:30:43 or zero, if that is the case, we want to set the video duration to be equal to video that duration.
00:30:51 So we don't want to keep the null values right here.
00:30:54 And I think we have to put this use effect after we actually declared the video.
00:30:58 So it's going to go right here.
00:31:00 Okay, so now we have the video duration right here that we're passing over to the save video details.
00:31:05 Now, I can notice that our video ID has a red squiggly line right here saying that argument type of unknown is not assignable to the parameter of type string.
00:31:15 So why does it think that this video ID that is getting returned from the git video upload URL is not actually a string?
00:31:23 We can head over into it.
00:31:25 And here we can say that the output of this fetch will be a bunny video response.
00:31:33 That way it'll know that what we're returning from here actually contains the video ID so it doesn't complain.
00:31:39 It knows that it is of a type string.
00:31:41 So if we do this, you can notice that we have a problem right here.
00:31:46 saying that there's a bit of a mismatch in the video details.
00:31:49 I think it's saying that we're missing the tags.
00:31:52 So if you head over to the index.d.ts, you'll notice that we're not actually using the tags.
00:31:58 So you can just remove them from here.
00:32:00 And now that error is gone, but it's complaining about the visibility.
00:32:04 Visibility not only needs to be a string, but also one of the two specific values, either private or public.
00:32:10 So if I head back over to visibility, and just say either public or private or string, I think then we might be good.
00:32:20 There we go.
00:32:21 So finally, once we upload the video, we should be able to push to that video details page.
00:32:28 So I'll use the router functionality right here by getting the router right at the top.
00:32:34 by saying const router is equal to use router.
00:32:39 This is a use router hook, which we have to import from Next Navigation.
00:32:43 And then after we actually submit the video, we can say router.push.
00:32:49 And we want to push over to forward slash video, forward slash video ID, which is going to lead us to that video's details page.
00:32:59 So what do you say that we give it a shot?
00:33:01 Back within our application, we can try filling out these details for the first time and see what happens.
00:33:07 We have made so many changes, so I would really not expect it to work on the first try, but who knows?
00:33:15 Let's give it a shot.
00:33:16 I'll head over to upload videos, and you can actually open up the current code folder, head over to public, assets, samples,
00:33:27 and then other samples, you should have access to a couple of videos from JavaScript Mastery, some illustrations from our upcoming courses on 3JS,
00:33:36 GSAP, and more.
00:33:37 So feel free to use one of the videos from here.
00:33:39 I believe this one is talking about 3JS, so I'll upload it.
00:33:45 There we go.
00:33:45 You can see how it appears right here immediately.
00:33:48 You can play it, check it out, change the volume or open it in full screen.
00:33:53 And then for the title, I will say 3JS animations.
00:33:58 And as the description, I'll say a JS Mastery Pro course on 3JS and GSAP.
00:34:07 Feel free to type whatever you want in here.
00:34:09 We can also upload the thumbnail.
00:34:13 So in this case, I'll choose any random thumbnail from here, no matter that it's not a match.
00:34:17 You can see that it shows up nicely and I'll make the video public.
00:34:22 And with that, I don't want to be pessimistic, but I will open up the terminal right here.
00:34:27 So just in case something goes wrong, I can get the error right here immediately.
00:34:31 So let me go ahead and click upload video.
00:34:34 You can see it says uploading and we got redirected to the video details page.
00:34:40 That is amazing.
00:34:41 This is already a very, very good sign.
00:34:44 Why?
00:34:45 Well, because the redirection is the last step of the last step.
00:34:51 So neither one of these functions seem to have failed.
00:34:54 That's great.
00:34:55 Of course, we don't yet see the video right here nor we can play it because we didn't implement the UI for it.
00:35:01 But the video should be there.
00:35:03 Is there a way for us to test it?
00:35:04 Well, just head over to Bunny, go to Stream, and open up the library, and check this out.
00:35:13 We have one video.
00:35:14 Its name is not 3JS.
00:35:17 Actually, we used the Bunny automatic metadata to create a new title, Mastering 3D Transformations in 3JS Position, Rotation,
00:35:26 and Scale Explained.
00:35:27 This is pretty cool.
00:35:29 And it even created a full description right here for this video, as well as generated the captions in the English language.
00:35:37 All of this is pretty cool, and you can see the video has been uploaded within our GUI, which means that it is actually there.
00:35:45 You can also head over to storage and you'll see that we have uploaded one thumbnail within the thumbnails folder.
00:35:52 And that is this one right here.
00:35:54 We can't seem to open it, but I believe it should be there.
00:35:57 What I care most about though is of course that we have this video right here.
00:36:01 So congrats on completing the most significant part of this course.
00:36:06 uploading the video.
00:36:08 Very, very soon, you'll be able to show that uploaded video right here to the homepage and to your profile page, if you're the user who uploaded it.
00:36:17 So take a bit of a break.
00:36:19 You deserved it.
00:36:20 Moving on, before we fetch that video on the homepage, I want to teach you how to make your app even more secure now that they're making all of these very
00:36:29 important fetch and server action calls.
00:36:32 But more on that in the next lesson.