{"id":34912,"date":"2024-07-31T07:00:00","date_gmt":"2024-07-31T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=34912"},"modified":"2024-07-31T17:51:47","modified_gmt":"2024-08-01T00:51:47","slug":"ai-auto-captions-composable-website-video-next-js","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js","title":{"rendered":"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js"},"content":{"rendered":"\n<p>Videos capture attention and tell stories in a way that text simply can&#8217;t. Reaching a wider audience with video means businesses should plan to increase accessibility for viewers who are deaf or hard of hearing by providing subtitles.<\/p>\n\n\n\n<p>In this blog post, we\u2019ll walk you through how to generate automatic captions for videos using <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/documentation\" rel=\"noreferrer noopener\">Cloudinary<\/a>&#8216;s AI features in Next.js.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/cloudinary.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">A Cloudinary account<\/a>.<\/li>\n\n\n\n<li>A basic understanding of Next.js and Typescript.<\/li>\n<\/ul>\n\n\n\n<p>The complete source code of this project is on <a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-auto-captions\" rel=\"noreferrer noopener\">Git<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-auto-captions\" rel=\"noreferrer noopener\">H<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-auto-captions\" rel=\"noreferrer noopener\">ub<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Project Setup and Installation<\/h2>\n\n\n\n<p>For simplicity, in this example, we\u2019ll clone a starter project. Fork and clone the project into the preferred folder.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span>&gt;<\/span>git clone https:\/\/github.com\/ugwutotheeshoes\/cloudinary-captions.git<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span>&gt;<\/span>cd cloudinary-captions<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then, install the necessary dependencies and render the project at <a href=\"https:\/\/localhost:3000\/\" target=\"_blank\" rel=\"noreferrer noopener\"><code>https:\/\/localhost:3000\/<\/code><\/a> in the browser with the command below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span>&gt;<\/span>npm install &amp;&amp; npm run dev<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With the project installation done, let\u2019s set up our Cloudinary account.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Activating the Cloudinary Add-On<\/h2>\n\n\n\n<p>After creating an account on <a href=\"https:\/\/cloudinary.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary<\/a>, we\u2019ll have access to the dashboard, where all the credentials we\u2019ll need to build this project will be available.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"302\" data-public-id=\"Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_302,c_scale\/f_auto,q_auto\/v1722386750\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png?_i=AA\" alt=\"Cloudinary dashboard\" class=\"wp-post-34912 wp-image-34913\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1722386750\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386750\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386750\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386750\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386750\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386750\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-1.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Cloudinary dashboard<\/figcaption><\/figure>\n\n\n\n<p><span class=\" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz81zriz78z7wz78zz85zz80ztz72zz81zz80zz86zz88zz90zi5hz69zdfkz65z4sz74zz88zz76zz80zz88z\">Next, we\u2019ll create a <span style=\"font-family: monospace; background-color: rgb(247, 249, 250); color: rgb(27, 39, 51); border: 1px solid rgba(208, 212, 217, 0.5); margin-left: -1px;\">.env.local<\/span> file in the root folder of our project to store the credentials.<\/span><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\n\/\/ .env.local\nNEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_key<\/span>&gt;<\/span>\nNEXT_PUBLIC_CLOUDINARY_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_secret<\/span>&gt;<\/span>\nCLOUDINARY_API_SECRET=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_cloud_name<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Never share your credentials publicly or commit them to Git repositories.<\/p>\n<\/div>\n\n\n<p>Before we can generate video captions automatically, we&#8217;ll need to activate the Cloudinary add-on. Let\u2019s navigate to the <strong>Add-on<\/strong> section in Cloudinary, click the <a href=\"https:\/\/cloudinary.com\/documentation\/google_ai_video_transcription_addon\" target=\"_blank\" rel=\"noreferrer noopener\">Google AI Video Transcription service<\/a>, and then subscribe to the free plan.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"511\" data-public-id=\"Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1722386745\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png?_i=AA\" alt=\"\" class=\"wp-post-34912 wp-image-34914\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1722386745\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386745\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386745\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386745\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386745\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386745\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-2.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Head to the Add-on section<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"511\" data-public-id=\"Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1722386740\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png?_i=AA\" alt=\"\" class=\"wp-post-34912 wp-image-34915\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1722386740\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386740\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386740\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386740\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386740\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1722386740\/Web_Assets\/blog\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3\/blog-Add-AI-Auto-Captions-to-a-Composable-Website-Video-Using-Cloudinary-in-Next.js-3.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Subscribe to the free plan<\/figcaption><\/figure>\n\n\n\n<p>The Google AI Video Transcription add-on automatically generates speech-to-text transcripts of videos. The add-on supports transcription of videos in any language. Now, we\u2019re ready to start building the app.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Building the App\u2019s Functionality<\/h2>\n\n\n\n<p>In our cloned project\u2019s codebase, an input element allows us to select the video we want to transcribe and to trigger the upload process by hitting the Upload button. Next, we\u2019ll navigate to the <code>src\/components\/Homepage.tsx<\/code> file. The current code structure should look like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> Image <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/image\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> styles <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'.\/Home.module.css'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Homepage<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> &#91;file, setFile] = useState&lt;File | <span class=\"hljs-literal\">null<\/span>&gt;(<span class=\"hljs-literal\">null<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> &#91;url, setUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> &#91;isLoading, setIsLoading] = useState&lt;<span class=\"hljs-built_in\">Boolean<\/span>&gt;(<span class=\"hljs-literal\">false<\/span>);\n  \n    <span class=\"hljs-keyword\">const<\/span> handleFileChange = <span class=\"hljs-function\">(<span class=\"hljs-params\">e: React.ChangeEvent&lt;HTMLInputElement&gt;<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> selectedFile = e.target.files?.&#91;<span class=\"hljs-number\">0<\/span>];\n      <span class=\"hljs-keyword\">if<\/span> (selectedFile) {\n        setFile(selectedFile);\n      }\n    };\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-bold text-3xl text-blue-700\"<\/span>&gt;<\/span>Wanderlust Travel<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-10\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-lg\"<\/span>&gt;<\/span>Discover Your Dream Destination<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    Wanderlust Travel curates unforgettable experiences around the globe.\n    From bustling cities to pristine beaches, we offer a variety of\n    tours and packages to suit every traveler<span class=\"hljs-symbol\">&amp;apos;<\/span>s taste and budget.\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  {\/* <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.exploreButton}<\/span>&gt;<\/span>Explore Destinations<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span> *\/}\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-10\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-lg\"<\/span>&gt;<\/span>Popular Destinations<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.cardContainer}<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.card}<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span>\n        <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\".\/paris.jpg\"<\/span> \/\/ <span class=\"hljs-attr\">Replace<\/span> <span class=\"hljs-attr\">with<\/span> <span class=\"hljs-attr\">your<\/span> <span class=\"hljs-attr\">destination<\/span> <span class=\"hljs-attr\">image<\/span>\n        <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Paris\"<\/span>\n        <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">{300}<\/span>\n        <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">{200}<\/span>\n      \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-xl\"<\/span>&gt;<\/span>Paris<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Experience the rich culture and vibrant history...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-10\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-lg\"<\/span>&gt;<\/span>Explore with Us<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>A captivating video showcasing travel experiences<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex pt-4 items-center mb-10\"<\/span>&gt;<\/span>\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"video\/*\"<\/span> <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleFileChange}<\/span> \/&gt;<\/span>\n\n    {\/* Space for your video *\/}\n    {isLoading ? <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.spinner}<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span> : <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'bg-blue-800 text-white p-2 rounded-md'<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> console.log('handleUpload')\n    }&gt;Upload<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>}\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.videoWrapper}<\/span>&gt;<\/span>\n    {\/* Replace with your video embed or component *\/}\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>A captivating video showcasing travel experiences<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-10\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-lg\"<\/span>&gt;<\/span>What Our Travelers Say<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Add testimonials from satisfied customers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-10\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-lg\"<\/span>&gt;<\/span>Book Your Dream Trip Today<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Contact us to start planning your unforgettable adventure.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.contactButton}<\/span>&gt;<\/span>Contact Us<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><a href=\"https:\/\/gist.github.com\/ugwutotheeshoes\/2af76d9e23dca5d9d32b74e2999de557\">https:\/\/gist.github.com\/ugwutotheeshoes\/2af76d9e23dca5d9d32b74e2999de557<\/a><\/p>\n\n\n\n<p>Next, we\u2019ll create a function within the <code>src\/components\/Homepage.tsx<\/code> file that handles, appends, and sends the video file to a route handler. First, the function will verify if a file is selected and stops if not. Then, we\u2019ll create a FormData object specifically for handling file uploads. We\u2019ll include the selected video in the FormData object under the key inputFile. Finally, we\u2019ll send a POST request to the \/api\/upload endpoint on the server, including the FormData object as the body. Then, we\u2019ll create an error handler in case of errors during upload.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ src\/components\/Homepage.tsx <\/span>\n<span class=\"hljs-keyword\">const<\/span> handleUpload = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    <span class=\"hljs-keyword\">if<\/span> (!file) <span class=\"hljs-keyword\">return<\/span>;\n    <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n    formData.append(<span class=\"hljs-string\">'inputFile'<\/span>, file);\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">'\/api\/upload'<\/span>, {\n        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'POST'<\/span>,\n        <span class=\"hljs-attr\">body<\/span>: formData,\n      });\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-comment\">\/\/ Handle network errors or other exceptions<\/span>\n      <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Error uploading file:'<\/span>, error);\n    }\n  };<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let\u2019s also link the function to the Upload button to fire off requests.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\n\/\/ src\/components\/Homepage.tsx\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'min-h-screen flex-col items-center justify-between p-10'<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"video\/*\"<\/span> <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleFileChange}<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'mb-10 bg-blue-800 text-white p-2 rounded-md'<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handleUpload}<\/span>&gt;<\/span>Upload<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n    ...\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Generating Automatic Captions<\/h2>\n\n\n\n<p>Here, we\u2019ll generate subtitles for our video file. Let&#8217;s create a new file in our <code>src<\/code> folder called <code>pages\/api\/upload.ts<\/code>. Then, import Cloudinary\u2019s Node.js SDK (version 2) and define its configurations using the details we added in our <code>.env.local<\/code> file.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ pages\/api\/upload.ts<\/span>\n<span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n<span class=\"hljs-comment\">\/\/ Configure Cloudinary with your account details<\/span>\ncloudinary.v2.config({\n    <span class=\"hljs-attr\">cloud_name<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,\n    <span class=\"hljs-attr\">api_key<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,\n    <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Uploading a large video file will require an extra step since it exceeds the body parser\u2019s data limit. <a target=\"_blank\" href=\"https:\/\/www.npmjs.com\/package\/@types\/formidable\" rel=\"noreferrer noopener\">Formidable<\/a> solves this issue and processes the video data efficiently. Let\u2019s create a Promise function, data, to handle asynchronous file parsing. The function will use a new IncomingForm instance to parse the incoming request data and resolve the Promise with an object containing the video file. Next, we\u2019ll access the video file with the key inputFile. We\u2019ll also turn off the automatic body-parser for API routes to avoid limitations on file size.<\/p>\n\n\n\n<p>After processing the video file, we&#8217;ll interact with Cloudinary&#8217;s API, using its <a href=\"https:\/\/cloudinary.com\/documentation\/upload_presets\" target=\"_blank\" rel=\"noreferrer noopener\">upload presets<\/a> and specific parameters to upload the video and automatically generate subtitles through transcription.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ pages\/api\/upload.ts<\/span>\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { IncomingForm } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'formidable'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Cloudinary configuration<\/span>\n    cloudinary.v2.config({\n      ...\n});\n<span class=\"hljs-comment\">\/\/ disables the automatic body parser for API routes<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> config = {\n  <span class=\"hljs-attr\">api<\/span>: {\n    <span class=\"hljs-attr\">bodyParser<\/span>: <span class=\"hljs-literal\">false<\/span>,\n  },\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">req: NextApiRequest, res: NextApiResponse<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> data: any = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve, reject<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-comment\">\/\/ handle file parsing<\/span>\n    <span class=\"hljs-keyword\">const<\/span> form = <span class=\"hljs-keyword\">new<\/span> IncomingForm();\n    form.parse(req, (err, fields, files) =&gt; {\n      <span class=\"hljs-keyword\">if<\/span> (err) <span class=\"hljs-keyword\">return<\/span> reject(err);\n      resolve({ fields, files });\n    });\n  });\n<span class=\"hljs-keyword\">const<\/span> file = data?.files?.inputFile&#91;<span class=\"hljs-number\">0<\/span>].filepath;\n    <span class=\"hljs-comment\">\/\/ Upload the video and generate video URL in a chain<\/span>\n    <span class=\"hljs-keyword\">await<\/span> cloudinary.v2.uploader\n        .upload(file, {\n            <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">\"my-video\"<\/span>,\n            <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n            <span class=\"hljs-attr\">raw_convert<\/span>: <span class=\"hljs-string\">'google_speech'<\/span>,\n        })\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The upload presets in the code snippet above consist of:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>public_id<\/code>. A custom public ID or name for the uploaded video.<\/li>\n\n\n\n<li><code>resource_type<\/code>. This specifies the resource type of the uploaded file.<\/li>\n\n\n\n<li><code>raw_convert<\/code>. This option enables raw video conversion using Google Speech.<\/li>\n<\/ul>\n\n\n\n<p>The <code>google_speech<\/code> parameter value triggers a call to <a href=\"https:\/\/cloud.google.com\/speech-to-text\" target=\"_blank\" rel=\"noreferrer noopener\">Google&#8217;s Cloud Speech-to-Text API<\/a>, transcribing the video. This transcription automatically generates captions for the video. In addition to automatic transcription, we can request a <a href=\"https:\/\/cloudinary.com\/documentation\/google_ai_video_transcription_addon#generating_standard_subtitle_formats\" target=\"_blank\" rel=\"noreferrer noopener\">standard subtitle format<\/a> such as <strong><code>vtt<\/code><\/strong> or <strong><code>srt<\/code><\/strong>. To do this, include the desired subtitle format in the google_speech parameter value like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\">\n <span class=\"hljs-selector-tag\">cloudinary<\/span><span class=\"hljs-selector-class\">.v2<\/span><span class=\"hljs-selector-class\">.uploader<\/span>\n      <span class=\"hljs-selector-class\">.upload<\/span>(<span class=\"hljs-selector-tag\">file<\/span>, { \n          <span class=\"hljs-attribute\">public_id<\/span>: <span class=\"hljs-string\">\"my-video\"<\/span>,\n          resource_type: <span class=\"hljs-string\">\"video\"<\/span>, \n          raw_convert: <span class=\"hljs-string\">\"google_speech:srt:vtt\"<\/span> \n      })<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Additionally, we&#8217;ll submit another request that embeds the video, transcript file, and some transformation settings. This request returns a URL that renders our video and its subtitles. The transformation options contain an overlay property to embed the specified subtitles file on the video and a flags property to ensure the overlay is applied during video processing.&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ pages\/api\/upload.ts <\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">req: NextApiRequest, res: NextApiResponse<\/span>) <\/span>{\n...\n+ <span class=\"hljs-keyword\">const<\/span> subtitlesOverlay = { <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"subtitles\"<\/span>, <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">\"video.transcript\"<\/span> }\n    <span class=\"hljs-comment\">\/\/ Set the transformation options for the video, including the subtitle overlay<\/span>\n+    <span class=\"hljs-keyword\">const<\/span> transformationOptions = &#91;\n+        { <span class=\"hljs-attr\">overlay<\/span>: subtitlesOverlay },\n+        { <span class=\"hljs-attr\">flags<\/span>: <span class=\"hljs-string\">\"layer_apply\"<\/span> }\n+    ];\n<span class=\"hljs-keyword\">await<\/span> cloudinary.v2.uploader\n        .upload(file, {\n            <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">\"my-video\"<\/span>,\n            <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n            <span class=\"hljs-attr\">raw_convert<\/span>: <span class=\"hljs-string\">'google_speech'<\/span>,\n        })\n+ .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">uploadResponse<\/span>) =&gt;<\/span> {\n            <span class=\"hljs-comment\">\/\/ Generate the video URL based on the upload response<\/span>\n+           <span class=\"hljs-keyword\">const<\/span> videoUrl = cloudinary.v2.url(videoPublicId, {\n+                resource_type: <span class=\"hljs-string\">'video'<\/span>,\n+                transformation: transformationOptions,\n+            });\n            <span class=\"hljs-comment\">\/\/ Return both upload response and video URL (can be modified to return only videoUrl as JSON)<\/span>\n+            <span class=\"hljs-keyword\">return<\/span> { uploadResponse, videoUrl };\n        })\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then, we\u2019ll return an object containing the following properties: a status code indicating success, a subtitled video URL, and an upload response.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ pages\/api\/upload.ts <\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">req: NextApiRequest, res: NextApiResponse<\/span>) <\/span>{\n<span class=\"hljs-comment\">\/\/ Upload and transcription requests<\/span>\n  ...\n.then(<span class=\"hljs-function\">(<span class=\"hljs-params\">combinedData<\/span>) =&gt;<\/span> {\n            <span class=\"hljs-comment\">\/\/ Handle the combined data (uploadResponse and videoUrl)<\/span>\n            <span class=\"hljs-built_in\">console<\/span>.log(combinedData); <span class=\"hljs-comment\">\/\/ Log for debugging purposes<\/span>\n            res.json(combinedData); <span class=\"hljs-comment\">\/\/ Return the combined data as JSON (modify object if needed)<\/span>\n        })\n        .catch(<span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> {\n            <span class=\"hljs-built_in\">console<\/span>.error(error);\n            res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message }); <span class=\"hljs-comment\">\/\/ Handle errors<\/span>\n        });\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><span class=\" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz81zriz78z7wz78zz85zz80ztz72zz81zz80zz86zz88zz90zi5hz69zdfkz65z4sz74zz88zz76zz80zz88z\">Finally, we\u2019ll return to the <span style=\"font-family: monospace; background-color: rgb(247, 249, 250); color: rgb(27, 39, 51); border: 1px solid rgba(208, 212, 217, 0.5); margin-left: -1px;\">src\/components\/Homepage.tsx<\/span> file to render the subtitled video.&nbsp;<\/span><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ src\/components\/Homepage.tsx<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;url, setUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n    ...\nconst handleUpload = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    ...\n    try {\n    ...\nif (response.ok) {\n        <span class=\"hljs-comment\">\/\/ Handle success, such as updating UI or showing a success message<\/span>\n        <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> response.json();\n        setUrl(data.videoUrl)\n        <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'File uploaded successfully:'<\/span>, data.url);\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-comment\">\/\/ Handle network errors or other exceptions<\/span>\n      <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Error uploading file:'<\/span>, error);\n    }\n  };\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'min-h-screen flex-col items-center justify-between p-10'<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-10\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"font-semibold text-lg\"<\/span>&gt;<\/span>Explore with Us<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.videoWrapper}<\/span>&gt;<\/span>\n        {\/* Space for your video *\/}\n  +        {url &amp;&amp;\n  +             <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">video<\/span> <span class=\"hljs-attr\">controls<\/span>&gt;<\/span>\n  +                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">source<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"mp4\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"video\/mp4\"<\/span> \/&gt;<\/span>\n  +              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">video<\/span>&gt;<\/span>}\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>A captivating video showcasing travel experiences<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n)}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Testing the Application<\/h2>\n\n\n\n<p>With all the necessary configurations set up, we can now test the app. We\u2019ll select a video to generate auto-captions and upload it to Cloudinary. Once the upload process is complete, we\u2019ll fetch and render the video and its transcript file. The app should look like this:<\/p>\n\n\n<cld-video-player\n      cloud-name='cloudinary-marketing'\n      public-id='blog-Add_AI_Auto-Captions_to_a_Composable_Website_Video_Using_Cloudinary_in_Next.js-4'\n      js-config='{\"playbackRates\":[0.5,1,1.5,2]}'\n      style='max-width: ;'\n      class='c-video-player'\n      \n      core-version='2.12.3'\n      player-version='1.7.0'\n      >\n      <video\n        id='_video-player69dfa16836925'\n        data-cld-big-play-button='init'\n        data-cld-source-types='[\"hls\",\"webm\\\/vp9\",\"mp4\\\/h265\",\"mp4\"]'\n        controls\n        muted\n        class='cld-video-player cld-fluid wp-block-cloudinary-video-player  cld-video-player-skin-dark'\n      ><\/video>\n    <\/cld-video-player>\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>This guide demonstrated how to easily add automatic captions to videos with Next.js and Cloudinary&#8217;s video features. This allows you to enhance user engagement and accessibility without the hassle of manual captioning. To extend the app\u2019s functionality, consider generating multilingual captions to cater to a global user base or full video transcripts in text formats for viewers who prefer written content.<\/p>\n\n\n\n<p>For more on Cloudinary AI can optimize your video workflow, improve efficiencies, and boost converesions, <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/\" rel=\"noreferrer noopener\">contact us today<\/a>.<\/p>\n\n\n\n<p>And if you found this blog post helpful and want to discuss it in more detail, join the <a target=\"_blank\" href=\"https:\/\/community.cloudinary.com\/\" rel=\"noreferrer noopener\"><u>Cloudinary Community forum<\/u><\/a> and its associated <a target=\"_blank\" href=\"https:\/\/discord.com\/invite\/cloudinary\" rel=\"noreferrer noopener\"><u>Discord<\/u><\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/cloudinary.com\/documentation\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\/google_ai_video_transcription_addon#displaying_transcripts_as_subtitle_overlays\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Google AI Video Transcription<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\/cloudinary_video_player\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Video Player<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\/video_manipulation_and_delivery\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Video Documentation<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Videos capture attention and tell stories in a way that text simply can&#8217;t. Reaching a wider audience with video means businesses should plan to increase accessibility for viewers who are deaf or hard of hearing by providing subtitles. In this blog post, we\u2019ll walk you through how to generate automatic captions for videos using Cloudinary&#8216;s [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":34937,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[336,212,305],"class_list":["post-34912","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-ai","tag-next-js","tag-video-api"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Add Auto-Captions in Next.js Using Cloudinary AI<\/title>\n<meta name=\"description\" content=\"Learn how to add automatic captions to videos with Next.js and Cloudinary&#039;s video features, enhancing user engagement and accessibility.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js\" \/>\n<meta property=\"og:description\" content=\"Learn how to add automatic captions to videos with Next.js and Cloudinary&#039;s video features, enhancing user engagement and accessibility.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-07-31T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-08-01T00:51:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js\",\"datePublished\":\"2024-07-31T14:00:00+00:00\",\"dateModified\":\"2024-08-01T00:51:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\"},\"wordCount\":987,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA\",\"keywords\":[\"AI\",\"Next.js\",\"Video API\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2024\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\",\"url\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\",\"name\":\"Add Auto-Captions in Next.js Using Cloudinary AI\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA\",\"datePublished\":\"2024-07-31T14:00:00+00:00\",\"dateModified\":\"2024-08-01T00:51:47+00:00\",\"description\":\"Learn how to add automatic captions to videos with Next.js and Cloudinary's video features, enhancing user engagement and accessibility.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Add Auto-Captions in Next.js Using Cloudinary AI","description":"Learn how to add automatic captions to videos with Next.js and Cloudinary's video features, enhancing user engagement and accessibility.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js","og_locale":"en_US","og_type":"article","og_title":"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js","og_description":"Learn how to add automatic captions to videos with Next.js and Cloudinary's video features, enhancing user engagement and accessibility.","og_url":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js","og_site_name":"Cloudinary Blog","article_published_time":"2024-07-31T14:00:00+00:00","article_modified_time":"2024-08-01T00:51:47+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog-jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js","datePublished":"2024-07-31T14:00:00+00:00","dateModified":"2024-08-01T00:51:47+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js"},"wordCount":987,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA","keywords":["AI","Next.js","Video API"],"inLanguage":"en-US","copyrightYear":"2024","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js","url":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js","name":"Add Auto-Captions in Next.js Using Cloudinary AI","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA","datePublished":"2024-07-31T14:00:00+00:00","dateModified":"2024-08-01T00:51:47+00:00","description":"Learn how to add automatic captions to videos with Next.js and Cloudinary's video features, enhancing user engagement and accessibility.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/ai-auto-captions-composable-website-video-next-js#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Add AI Auto-Captions to a Composable Website Video Using Cloudinary in Next.js"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720046145\/ai_caption_video_nextjs-blog\/ai_caption_video_nextjs-blog.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34912","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=34912"}],"version-history":[{"count":4,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34912\/revisions"}],"predecessor-version":[{"id":34936,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34912\/revisions\/34936"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/34937"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=34912"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=34912"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=34912"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}