{"id":36898,"date":"2025-02-21T07:00:00","date_gmt":"2025-02-21T15:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=36898"},"modified":"2025-02-21T12:06:13","modified_gmt":"2025-02-21T20:06:13","slug":"generate-video-summary-openai","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai","title":{"rendered":"Generate a Video Summary With OpenAI"},"content":{"rendered":"\n<p>Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video transcripts into a note-taking tool \u2014 or hunting down written summaries \u2014 is one way to go about it, but it\u2019s not exactly efficient, or, let\u2019s be honest, fun. This is where AI technology steps in to do the heavy lifting for us.\u00a0<\/p>\n\n\n\n<p>In this blog post, we\u2019ll use Cloudinary&#8217;s video transcription services and <a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">OpenAI&#8217;s <\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">l<\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">arge <\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">l<\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">anguage <\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">m<\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">odel<\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\"> <\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">(<\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">LLM<\/a><a target=\"_blank\" href=\"https:\/\/platform.openai.com\/docs\/models\" rel=\"noreferrer noopener\">)<\/a> capabilities to generate accurate transcriptions and concise summaries.&nbsp;<\/p>\n\n\n\n<p>To follow this tutorial, you&#8217;ll need <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/\" rel=\"noreferrer noopener\">a Cloudinary account<\/a> to handle video uploads and transcriptions, <a target=\"_blank\" href=\"https:\/\/chatgpt.com\/\" rel=\"noreferrer noopener\">a ChatGPT Plus account<\/a> to access the text generation API, and a basic understanding of Next.js and TypeScript.&nbsp;<\/p>\n\n\n\n<p>The complete source code for the project is available on <a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-video-summary-openai\" rel=\"noreferrer noopener\">GitHub<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up a Next.js Project<\/h2>\n\n\n\n<p>To get started, create a Next.js app by running the following command:<\/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>npx create-next-app@14.2 cloudinary-video-summary<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>When prompted, select <strong>TypeScript<\/strong>, <strong>Tailwind CSS<\/strong>, and <strong>App Router<\/strong> to set them up in your application.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1740166504\/blog-Generate_a_Video_Summary_With_OpenAI-1.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>Next, navigate to the newly created app folder:<\/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>cd cloudinary-video-summary<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>Install the required dependencies, including the <strong>Cloudinary Node.js SDK<\/strong> and the <strong>OpenAI client library<\/strong>:<\/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\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span>&gt;<\/span>npm install cloudinary openai<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/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\n<p>Once your project is set up, run the development server:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" 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 run dev<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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>Your Next.js project should now be running at <code>http:\/\/localhost:3000<\/code>.&nbsp;<\/p>\n\n\n\n<p>After initializing the project, navigate to <a target=\"_blank\" href=\"https:\/\/console.cloudinary.com\/pm\/c-44312a85dae383cdb3fedab809bc64\/developer-dashboard\" rel=\"noreferrer noopener\">Cloudinary\u2019s developer dashboard<\/a> to get all the required credentials for this project.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1740166505\/blog-Generate_a_Video_Summary_With_OpenAI-2.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Cloudinary Dashboard<\/figcaption><\/figure>\n\n\n\n<p>To access the text generation capabilities, you\u2019ll need an OpenAI API key. Go to the <a href=\"https:\/\/platform.openai.com\/account\/api-keys\" target=\"_blank\" rel=\"noreferrer noopener\">OpenAI API key page<\/a> to create a new API key. On the API key page, click <strong>Create new secret key<\/strong>. This key will enable <a href=\"https:\/\/platform.openai.com\/docs\/guides\/text-generation\/building-prompts\" target=\"_blank\" rel=\"noreferrer noopener\">text generation<\/a> interaction with the OpenAI API.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1740166505\/blog-Generate_a_Video_Summary_With_OpenAI-3.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Create an API Key<\/figcaption><\/figure>\n\n\n\n<p>In your project\u2019s root directory, create a .env.local file and store the Cloudinary and Open API key credentials, as shown below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/\/ .env.local\n\nNEXT_PUBLIC_OPENAI_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">openai_api_key<\/span>&gt;<\/span>\n\nNEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_key<\/span>&gt;<\/span>\n\nNEXT_PUBLIC_CLOUDINARY_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_secret<\/span>&gt;<\/span>\n\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-5\"><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.<\/p>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Activating the Video Transcription Add-On<\/h2>\n\n\n\n<p>Before creating a transcript, you\u2019ll need to activate the Cloudinary Google AI Video Transcription add-on. Cloudinary provides automatic video transcription services using the <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/documentation\/google_ai_video_transcription_addon\" rel=\"noreferrer noopener\">Google AI Video Transcription add-on<\/a>, generating speech-to-text transcripts of videos. The add-on supports transcription of videos in any language.<\/p>\n\n\n\n<p>Navigate to the <strong>Add-on<\/strong> section in Cloudinary, click the <strong>Google AI Video Transcription<\/strong> service, and 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\/image_368992aec9\/image_368992aec9.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1740168213\/Web_Assets\/blog\/image_368992aec9\/image_368992aec9.png?_i=AA\" alt=\"This image has an empty alt attribute; its file name is blog-Generate_a_Video_Summary_With_OpenAI-4.png\" class=\"wp-post-36898 wp-image-36899\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1740168213\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1740168213\/Web_Assets\/blog\/image_368992aec9\/image_368992aec9.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1740168213\/Web_Assets\/blog\/image_368992aec9\/image_368992aec9.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1740168213\/Web_Assets\/blog\/image_368992aec9\/image_368992aec9.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1740168213\/Web_Assets\/blog\/image_368992aec9\/image_368992aec9.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1740168213\/Web_Assets\/blog\/image_368992aec9\/image_368992aec9.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 decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1740166505\/blog-Generate_a_Video_Summary_With_OpenAI-5.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Subscribe to the Free plan<\/figcaption><\/figure>\n\n\n\n<p>Activating the video transcript Add-on will set up the transcription service needed to transcribe the uploaded video in the next step.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Upload a Video to Cloudinary<\/h2>\n\n\n\n<p>Next, you&#8217;ll need to upload a video file to initiate the transcription process. To do this, you\u2019ll create a form with an input element to accept the video file you want to transcribe and a button to trigger the upload process.&nbsp;<\/p>\n\n\n\n<p>Navigate to <code>pages.tsx<\/code> file in the <code>src\/app<\/code> folder and create the form and input element, as shown below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/pages.tsx<\/span>\n\n<span class=\"hljs-string\">\"use client\"<\/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\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0 <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"grid grid-rows-&#91;20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-&#91;family-name:var(--font-geist-sans)]\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">nav<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex justify-center my-10 items-center rounded-md border-&#91;1px] px-20 py-2 border-blue-800\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <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\">name<\/span>=<span class=\"hljs-string\">\"video\"<\/span> <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"video\/*\"<\/span> \/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <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 uppercase tracking-wider text-sm\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Upload\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 {!summary &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-xl tracking-wide font-semibold text-center\"<\/span>&gt;<\/span>Upload a video to get an instant summary.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>}\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">nav<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0 );\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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>Next, create a function within the same pages.tsx file that handles, appends, and sends the video file to an API route. Then, connect the function to the form&#8217;s submission event to trigger it upon submission.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/pages.tsx\u00a0<\/span>\n\nexport <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>{\u00a0\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;isLoading, setIsLoading] = useState&lt;boolean&gt;(<span class=\"hljs-keyword\">false<\/span>);\n\n<span class=\"hljs-keyword\">const<\/span> handleUpload = async (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {\n\n\u00a0 \u00a0 e.preventDefault(); \u00a0<span class=\"hljs-comment\">\/\/ Prevents the form from refreshing the page<\/span>\n\n\u00a0 \u00a0 setIsLoading(<span class=\"hljs-keyword\">true<\/span>)\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData(e.currentTarget);\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> response = await fetch(<span class=\"hljs-string\">'\/api\/upload'<\/span>, {\n\n\u00a0 \u00a0 \u00a0 \u00a0 method: <span class=\"hljs-string\">'POST'<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 body: formData,\n\n\u00a0 \u00a0 \u00a0 });\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> (response.ok) {\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> data = await response.json();\n\n\u00a0 \u00a0 \u00a0 \u00a0 console.log(<span class=\"hljs-string\">'Upload successful'<\/span>, data);\n\n\u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error) {\n\n\u00a0 \u00a0 \u00a0 console.error(<span class=\"hljs-string\">'Error uploading file:'<\/span>, error);\n\n\u00a0 \u00a0 }\n\n\u00a0 };\n\n\u00a0 <span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0 ...\n\n\u00a0 \u00a0 &lt;form onSubmit={handleUpload}&gt;\n\n\u00a0 \u00a0 ... <span class=\"hljs-comment\">\/\/ input and button elements<\/span>\n\n\u00a0 \u00a0 &lt;\/form&gt;\n\n\u00a0 ...\n\n\u00a0 )}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the code above, a <code>FormData<\/code> object is created to extract the video from the form automatically. Then, the form data is sent to the <code>\/api\/upload<\/code> endpoint on the server via a POST request. Finally, the response is logged to the console to see if the upload is successful.<\/p>\n\n\n\n<p>Next, you\u2019ll define an API route handler file to upload the video to Cloudinary. Navigate to the <code>src\/app<\/code> folder and create a folder called api. In the api folder, create another folder called upload. Then, add a file called <code>route.ts<\/code> and add the following code:&nbsp;<\/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\"><span class=\"hljs-comment\">\/\/src\/api\/upload\/route.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { NextResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next\/server'<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Configure Cloudinary with your account details<\/span>\n\ncloudinary.v2.config({\n\n\u00a0 <span class=\"hljs-attr\">cloud_name<\/span>: process.env.CLOUDINARY_CLOUD_NAME,\n\n\u00a0 <span class=\"hljs-attr\">api_key<\/span>: process.env.CLOUDINARY_API_KEY,\n\n\u00a0 <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n\n});\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">POST<\/span>(<span class=\"hljs-params\">req: Request, res: NextResponse<\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">await<\/span> req.formData();\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> file = formData.get(<span class=\"hljs-string\">'video'<\/span>) <span class=\"hljs-keyword\">as<\/span> File;\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> buffer: Buffer = Buffer.from(<span class=\"hljs-keyword\">await<\/span> file.arrayBuffer());\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> cloud_name: string | <span class=\"hljs-literal\">undefined<\/span> = process.env.CLOUDINARY_CLOUD_NAME;\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> videoFile: string = <span class=\"hljs-string\">`data:<span class=\"hljs-subst\">${file.type}<\/span>;base64,<span class=\"hljs-subst\">${buffer.toString(\n\n\u00a0 \u00a0 <span class=\"hljs-string\">'base64'<\/span>\n\n\u00a0 )}<\/span>`<\/span>;\n\n\u00a0 <span class=\"hljs-comment\">\/\/ Upload the video and generate the transcript URL in a chain<\/span>\n\n\u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> uploadResult = <span class=\"hljs-keyword\">await<\/span> cloudinary.v2.uploader\n\n\u00a0 \u00a0 \u00a0 .upload(videoFile, {\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">`videos\/<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">Date<\/span>.now()}<\/span>`<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">raw_convert<\/span>: <span class=\"hljs-string\">'google_speech'<\/span>,\n\n\u00a0 \u00a0 \u00a0 })\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> videoUrl = uploadResult.secure_url;\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> transcriptFileUrl = <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/<span class=\"hljs-subst\">${cloud_name}<\/span>\/raw\/upload\/v<span class=\"hljs-subst\">${uploadResult.version + <span class=\"hljs-number\">1<\/span>\n\n\u00a0 \u00a0 \u00a0 }<\/span>\/<span class=\"hljs-subst\">${uploadResult.public_id}<\/span>.transcript`<\/span>;\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> NextResponse.json(\n\n\u00a0 \u00a0 \u00a0 { uploadResult, transcriptFileUrl, videoUrl },\n\n\u00a0 \u00a0 \u00a0 { <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-number\">200<\/span> }\n\n\u00a0 \u00a0 );\n\n\u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> NextResponse.json({ <span class=\"hljs-attr\">message<\/span>: error.message }, { <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-number\">500<\/span> })\n\n\u00a0 }\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>Let\u2019s break down the actions taken in each section of the code snippet above:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Imports the necessary components from Next.js to define the data structure for requests and responses.<\/li>\n\n\n\n<li><code>cloudinary.v2.config({...})<\/code> imports the <a href=\"https:\/\/cloudinary.com\/documentation\/node_integration\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Node.js SDK (version 2)<\/a> configured using the previously set environment variables, including the Cloudinary account details.<\/li>\n\n\n\n<li>Parses the incoming request body and extracts the form data. Then, it creates a <strong>Buffer<\/strong> object from the uploaded file&#8217;s binary data and converts the file content into a base64 encoded string for efficient upload.<\/li>\n\n\n\n<li><code>try {...}<\/code> sends the video file to Cloudinary using the upload method. It provides options:\n<ul class=\"wp-block-list\">\n<li><code>public_id<\/code>: The unique public ID generated earlier.<\/li>\n\n\n\n<li><code>resource_type<\/code>: Set to &#8220;video&#8221; to indicate the upload type.<\/li>\n\n\n\n<li><code>raw_convert<\/code>: Set to <strong>google_speech<\/strong> to trigger 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.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code>const videoUrl<\/code> accesses the uploaded video from Cloudinary.<\/li>\n\n\n\n<li><code>const transcriptFileUrl<\/code> constructs a URL to access the transcript file based on the public ID and Cloudinary versioning.<\/li>\n\n\n\n<li><code>return NextResponse.json(\u2026);<\/code> handles the upload response. If the upload is successful, this line sends a 200 status code response with the transcript file URL, uploaded video and upload result object from Cloudinary.<\/li>\n\n\n\n<li><code>catch (error: any) {\u2026}<\/code> handles any errors that occur during the upload process.<\/li>\n<\/ul>\n\n\n\n<p>This setup allows for easy video upload and automatic transcription generation using Cloudinary\u2019s features, leveraging Next.js API routes to handle requests.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fetch the Transcript file<\/h2>\n\n\n\n<p>Currently, the transcript file can\u2019t be directly accessed immediately after uploading. This is due to Cloudinary&#8217;s asynchronous process of generating transcripts, which initially remains in a pending state as the time required to generate the transcript file depends on the duration of the video.<\/p>\n\n\n\n<p>To handle this, you\u2019ll need to check the transcription status from Cloudinary at predefined intervals. Add a function called <code>generateSummary<\/code> to the <code>src\/app\/pages.tsx<\/code> file, as shown below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/pages.tsx \u00a0<\/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\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;isLoading, setIsLoading] = useState&lt;boolean&gt;(<span class=\"hljs-literal\">false<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;videoUrl, setVideoUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">''<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> POLLING_INTERVAL = <span class=\"hljs-number\">30000<\/span>;\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> generateSummary = <span class=\"hljs-keyword\">async<\/span> (url: string) =&gt; {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-string\">`\/api\/summary?url=<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">encodeURIComponent<\/span>(url)}<\/span>`<\/span>\n\n\u00a0 \u00a0 \u00a0 );\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> response.json();\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.log(data);\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> (data.available) {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-comment\">\/\/ \u00a0the summary will be updated here<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 setIsLoading(<span class=\"hljs-literal\">false<\/span>)\n\n\u00a0 \u00a0 \u00a0 } <span class=\"hljs-keyword\">else<\/span> {\n\n\u00a0 \u00a0 \u00a0 \u00a0 setTimeout(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> generateSummary(url), POLLING_INTERVAL); \u00a0 \u00a0 \u00a0 \u00a0\n\n\u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Error checking transcription status:'<\/span>, error);\n\n\u00a0 \u00a0 }\n\n\u00a0 };\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> handleUpload = <span class=\"hljs-keyword\">async<\/span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {\n\n\u00a0 \u00a0 \u00a0 \u00a0 ...\n\n\u00a0if (response.ok) {\n\n\u00a0 \u00a0 \u00a0 \u00a0 ...\n\n\u00a0 \u00a0 \u00a0 \u00a0 generateSummary(data.transcriptFileUrl);\n\n\u00a0 \u00a0 \u00a0 \u00a0 setVideoUrl(data.videoUrl);\n\n\u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error) {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Error uploading file:'<\/span>, error);\n\n\u00a0 \u00a0 }\n\n\u00a0 };\n\n\u00a0 <span class=\"hljs-keyword\">return<\/span> (\n\n...\n\n\u00a0 )};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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 function uses a <a href=\"https:\/\/www.geeksforgeeks.org\/polling-in-system-design\/\" target=\"_blank\" rel=\"noreferrer noopener\">polling mechanism<\/a> set to 30000 milliseconds (or 30 seconds) to check for the availability of the transcript file. First, it sends a GET request to the <code>\/api\/summary<\/code> endpoint, passing the transcript file URL as the <code>url<\/code> parameter, encoded using <code>encodeURIComponent<\/code> to ensure it&#8217;s safe for use in the request. Then, the generateSummary function is triggered upon a successful video upload, receiving the transcript file URL as an input. The uploaded video from the data response is also stored in a state variable, <code>setVideoUrl<\/code>, which will be rendered once the transcript file is available. The app will recheck the transcript file status every five seconds until the transcript file is ready.<\/p>\n\n\n\n<p>The next step involves defining the structure of the transcript file and its words and creating a transcript parser to process the transcript data, converting it into a clear, readable text format. To define the structure of the transcript file, navigate to the <code>src<\/code> folder, create a folder called types, and then add a file called <code>transcript-data.type.ts<\/code>. Add the following code snippet:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/types\/transcript-data.type.ts<\/span>\n\nexport <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">Word<\/span> <\/span>{\n\n\u00a0 word: string;\n\n\u00a0 start_time: number;\n\n\u00a0 end_time: number;\n\n}\n\nexport <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">TranscriptData<\/span> <\/span>{\n\n\u00a0 transcript: string;\n\n\u00a0 confidence: number;\n\n\u00a0 words: Word&#91;];\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\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>These interfaces are used to type-check and model transcript data, ensuring the format is consistent throughout the application.<\/p>\n\n\n\n<p>Next, you\u2019ll create the transcript parser. To do this, head to the <code>src<\/code> folder and create a file called <code>transcript.ts<\/code>, then add the code snippet shown below.<\/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\"><span class=\"hljs-comment\">\/\/ src\/transcript.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { TranscriptData } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@\/types\/transcript-data.type'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> parseTranscriptData = <span class=\"hljs-keyword\">async<\/span> (\n\n\u00a0 data: TranscriptData&#91;]\n\n): <span class=\"hljs-built_in\">Promise<\/span>&lt;string&gt; =&gt; {\n\n\u00a0 <span class=\"hljs-keyword\">let<\/span> transcript: string = <span class=\"hljs-string\">''<\/span>;\n\n\u00a0 data.forEach(\n\n\u00a0 \u00a0 <span class=\"hljs-function\">(<span class=\"hljs-params\">line: TranscriptData<\/span>) =&gt;<\/span> (transcript = transcript + <span class=\"hljs-string\">` <span class=\"hljs-subst\">${line.transcript}<\/span>`<\/span>)\n\n\u00a0 );\n\n\u00a0 <span class=\"hljs-keyword\">return<\/span> transcript;\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>The code snippet above takes an array of <code>TranscriptData<\/code> objects and concatenates the transcript strings from each object into a single string.<\/p>\n\n\n\n<p>Additionally, you\u2019ll create a file for the <code>\/api\/summary<\/code> endpoint. Here, the transcript summary is generated once the transcript file is ready. Navigate to the <code>src\/app\/api<\/code> folder and create a new folder called summary. In this folder, create a new file called <code>route.ts<\/code>, then add the code snippet, as shown below.<\/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\"><span class=\"hljs-comment\">\/\/ src\/app\/api\/summary\/route.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { parseTranscriptData } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@\/transcript'<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { TranscriptData } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@\/types\/transcript-data.type'<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { NextResponse, type NextRequest } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next\/server'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">GET<\/span>(<span class=\"hljs-params\">req: NextRequest<\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> searchParams = req.nextUrl.searchParams;\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> url: string | <span class=\"hljs-literal\">null<\/span> = searchParams.get(<span class=\"hljs-string\">'url'<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(url <span class=\"hljs-keyword\">as<\/span> string);\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> (response.ok) {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> transcriptData: TranscriptData&#91;] = <span class=\"hljs-keyword\">await<\/span> response.json();\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> transcript: string = <span class=\"hljs-keyword\">await<\/span> parseTranscriptData(transcriptData);\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> NextResponse.json(\n\n\u00a0 \u00a0 \u00a0 \u00a0 { <span class=\"hljs-attr\">available<\/span>: <span class=\"hljs-literal\">true<\/span>},\n\n\u00a0 \u00a0 \u00a0 \u00a0 { <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-number\">200<\/span> }\n\n\u00a0 \u00a0 \u00a0 );\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">else<\/span> {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> NextResponse.json({ <span class=\"hljs-attr\">available<\/span>: <span class=\"hljs-literal\">false<\/span> }, { <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-number\">400<\/span> });\n\n\u00a0 \u00a0 }\n\n\u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(error);\n\n\u00a0 }\n\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<p>The code snippet above begins by importing the <code>parseTranscriptData<\/code> method and the <code>TranscriptData<\/code> interface. It then extracts the transcript URL from the incoming request&#8217;s query parameters.<\/p>\n\n\n\n<p>Then, it fetches the transcript data from the transcript file URL and parses the JSON response. The parsed data is processed using the <code>parseTranscriptData<\/code> function to extract the relevant transcript information.&nbsp;<\/p>\n\n\n\n<p>Finally, a JSON response is returned, indicating whether the transcript is available, and if not, the app will continue polling the <code>\/api\/summary<\/code> endpoint until the transcript file is generated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using OpenAI for Summary Generation<\/h2>\n\n\n\n<p>Once the transcript file is available, the next step is to send it to the <a href=\"https:\/\/platform.openai.com\/docs\/guides\/text-generation\/building-prompts\" target=\"_blank\" rel=\"noreferrer noopener\">OpenAI Text Generation model<\/a> for summarization and return the transcript summary. Update the code base in the <code>summary\/route.ts<\/code> file, as shown below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/api\/summary\/route.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> OpenAI <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"openai\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> openai = <span class=\"hljs-keyword\">new<\/span> OpenAI();\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">GET<\/span>(<span class=\"hljs-params\">req: NextRequest<\/span>) <\/span>{\n\n\u00a0 ... <span class=\"hljs-comment\">\/\/ Cloudinary video upload function<\/span>\n\n<span class=\"hljs-keyword\">if<\/span> (response.ok) {\n\n\u00a0 \u00a0 \u00a0 ...\n\n\u00a0 \u00a0 \u00a0const completion = <span class=\"hljs-keyword\">await<\/span> openai.chat.completions.create({\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">model<\/span>: <span class=\"hljs-string\">\"gpt-4\"<\/span>, <span class=\"hljs-comment\">\/\/ Choose the appropriate model<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">messages<\/span>: &#91;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">\"system\"<\/span>, <span class=\"hljs-attr\">content<\/span>: <span class=\"hljs-string\">\"You are a helpful assistant that summarizes texts.\"<\/span> },\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">\"user\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">content<\/span>: <span class=\"hljs-string\">`Please summarize the following transcript:<span class=\"hljs-subst\">${transcript}<\/span>`<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 },\n\n\u00a0 \u00a0 \u00a0 \u00a0 ],\n\n\u00a0 \u00a0 \u00a0 });\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> summary = completion.choices&#91;<span class=\"hljs-number\">0<\/span>].message.content;\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> NextResponse.json(\n\n\u00a0 \u00a0 \u00a0 \u00a0 { <span class=\"hljs-attr\">available<\/span>: <span class=\"hljs-literal\">true<\/span>, summary },\n\n\u00a0 \u00a0 \u00a0 \u00a0 { <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-number\">200<\/span> }\n\n\u00a0 \u00a0 \u00a0 );\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">else<\/span> {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> NextResponse.json({ <span class=\"hljs-attr\">available<\/span>: <span class=\"hljs-literal\">false<\/span> }, { <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-number\">200<\/span> });\n\n\u00a0 \u00a0 }\n\n\u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(error);\n\n\u00a0 }\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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>Here&#8217;s a breakdown of the code snippet above:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The code fetches the transcript data from the transcript file URL and parses it into a suitable format.&nbsp;<\/li>\n\n\n\n<li>The parsed transcript data is input to <a href=\"https:\/\/platform.openai.com\/docs\/guides\/text-generation\/building-prompts\" target=\"_blank\" rel=\"noreferrer noopener\">OpenAI\u2019s Text Generation<\/a> API. This API call sends a prompt to the OpenAI language model, asking it to summarize the provided transcript.&nbsp;<\/li>\n\n\n\n<li>Then, the API returns a response containing the generated summary along with an available flag from Cloudinary set to true indicating that the transcript is available, making the app stop the polling mechanism.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Display the Summary<\/h2>\n\n\n\n<p>Finally, you\u2019ll display the uploaded video and summary of the provided transcript. Head to the <code>pages.tsx<\/code> file in the src\/app directory and update the file with the code snippet below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/\/src\/app\/pages.tsx\n\nimport { useState } from 'react';\n\nexport default function Home() {\u00a0\n\n\u00a0 const &#91;videoUrl, setVideoUrl] = useState<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">string<\/span>&gt;<\/span>('');\n\n\u00a0 const &#91;summary, setSummary] = useState('');\u00a0\n\n\u00a0 const &#91;isLoading, setIsLoading] = useState<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">boolean<\/span>&gt;<\/span>(false);\n\n\u00a0 const generateSummary = async (url: string) =&gt; {\n\n\u00a0 \u00a0 try {\n\n\u00a0 \u00a0 \u00a0 ...\n\n\u00a0 \u00a0 \u00a0 if (data.available) {\n\n\u00a0 \u00a0 \u00a0 \u00a0 setSummary(data.summary)\n\n\u00a0 \u00a0 \u00a0 \u00a0 setIsLoading(false)\n\n\u00a0 \u00a0 \u00a0 } else {\n\n\u00a0 \u00a0 \u00a0 \u00a0 setTimeout(() =&gt; generateSummary(url), POLLING_INTERVAL); \u00a0 \u00a0 \u00a0 \u00a0\n\n\u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 } catch (error: any) {\n\n\u00a0 \u00a0 \u00a0 console.error('Error checking transcription status:', error);\n\n\u00a0 \u00a0 }\n\n\u00a0 };\n\n\u00a0 \u00a0 ... \/\/ upload function\n\n\u00a0return (\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"grid grid-rows-&#91;20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-&#91;family-name:var(--font-geist-sans)]\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0... \/\/form\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex flex-col gap-8 row-start-2 items-center sm:items-start\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 {isLoading &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Loader<\/span> <span class=\"hljs-attr\">size<\/span>=<span class=\"hljs-string\">{100}<\/span> \/&gt;<\/span>}\n\n\u00a0 \u00a0 \u00a0 \u00a0 {summary &amp;&amp; (\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-3xl font-semibold text-center mb-3\"<\/span>&gt;<\/span>Video<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles&#91;<\/span>'<span class=\"hljs-attr\">video-transcription-section<\/span>']}&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">video<\/span> <span class=\"hljs-attr\">crossOrigin<\/span>=<span class=\"hljs-string\">'anonymous'<\/span> <span class=\"hljs-attr\">controls<\/span> <span class=\"hljs-attr\">muted<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">source<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{videoUrl}<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'video\/mp4'<\/span> \/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">video<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"px-10 \"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-3xl font-semibold text-center mb-3\"<\/span>&gt;<\/span>Video Summary<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{summary}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 )}\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 );\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><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<h1 class=\"wp-block-heading\">The Results<\/h1>\n\n\n\n<p>To test the app, the user selects and uploads a video to Cloudinary, which is processed for transcription during the upload. After the upload is complete, the transcript file is fetched and parsed into a readable format and then sent to the OpenAI API for summarization.<\/p>\n\n\n<cld-video-player\n      cloud-name='cloudinary-marketing'\n      public-id='cloudinary-openai-summary'\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-player69d8d67aecad9'\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<h1 class=\"wp-block-heading\">Wrapping Up<\/h1>\n\n\n\n<p>By integrating Cloudinary and OpenAI, we provided a comprehensive solution to manual video summarization. You now have the tools to effortlessly upload videos, generate transcriptions, and create summaries using advanced AI capabilities. <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/users\/register_free\" rel=\"noreferrer noopener\">Sign up<\/a> for a free Cloudinary account today and get started.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video transcripts into a note-taking tool \u2014 or hunting down written summaries \u2014 is one way to go about it, but it\u2019s not exactly efficient, or, let\u2019s be honest, fun. This is where AI technology steps [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":36901,"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,303],"class_list":["post-36898","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-ai","tag-next-js","tag-video"],"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>Generate a Video Summary With OpenAI<\/title>\n<meta name=\"description\" content=\"Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video\" \/>\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\/generate-video-summary-openai\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Generate a Video Summary With OpenAI\" \/>\n<meta property=\"og:description\" content=\"Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-21T15:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-21T20:06:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI-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\/generate-video-summary-openai#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Generate a Video Summary With OpenAI\",\"datePublished\":\"2025-02-21T15:00:00+00:00\",\"dateModified\":\"2025-02-21T20:06:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai\"},\"wordCount\":1531,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA\",\"keywords\":[\"AI\",\"Next.js\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai\",\"url\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai\",\"name\":\"Generate a Video Summary With OpenAI\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA\",\"datePublished\":\"2025-02-21T15:00:00+00:00\",\"dateModified\":\"2025-02-21T20:06:13+00:00\",\"description\":\"Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Generate a Video Summary With OpenAI\"}]},{\"@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":"Generate a Video Summary With OpenAI","description":"Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video","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\/generate-video-summary-openai","og_locale":"en_US","og_type":"article","og_title":"Generate a Video Summary With OpenAI","og_description":"Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video","og_url":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai","og_site_name":"Cloudinary Blog","article_published_time":"2025-02-21T15:00:00+00:00","article_modified_time":"2025-02-21T20:06:13+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI-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\/generate-video-summary-openai#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Generate a Video Summary With OpenAI","datePublished":"2025-02-21T15:00:00+00:00","dateModified":"2025-02-21T20:06:13+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai"},"wordCount":1531,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA","keywords":["AI","Next.js","Video"],"inLanguage":"en-US","copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai","url":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai","name":"Generate a Video Summary With OpenAI","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA","datePublished":"2025-02-21T15:00:00+00:00","dateModified":"2025-02-21T20:06:13+00:00","description":"Manually summarizing videos can be time-consuming and inefficient, making it challenging to extract key information quickly. Copying and pasting video","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/generate-video-summary-openai"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/generate-video-summary-openai#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Generate a Video Summary With OpenAI"}]},{"@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\/v1736542467\/Video_Summary_Generation_With_OpenAI\/Video_Summary_Generation_With_OpenAI.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/36898","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=36898"}],"version-history":[{"count":2,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/36898\/revisions"}],"predecessor-version":[{"id":36902,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/36898\/revisions\/36902"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/36901"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=36898"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=36898"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=36898"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}