{"id":55088,"date":"2026-03-26T12:28:53","date_gmt":"2026-03-26T19:28:53","guid":{"rendered":"https:\/\/griddb.net\/?p=55088"},"modified":"2026-03-26T12:28:53","modified_gmt":"2026-03-26T19:28:53","slug":"generate-fun-ai-videos-from-a-photo-with-kling","status":"publish","type":"post","link":"https:\/\/www.griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/","title":{"rendered":"Generate Fun AI Videos from a Photo with Kling"},"content":{"rendered":"<h2 id=\"table-of-contents\">Table of Contents<\/h2>\n<ul>\n<li><a href=\"#prerequisites\">Prerequisites<\/a>\n<ul>\n<li><a href=\"#nodejs\">Node.js<\/a><\/li>\n<li><a href=\"#griddb\">GridDB<\/a><\/li>\n<li><a href=\"#fal-kling-21-api\">Fal Kling 2.1 API<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#how-to-run\">How to Run<\/a>\n<ul>\n<li><a href=\"#1-clone-the-repository\">1. Clone the repository<\/a><\/li>\n<li><a href=\"#2-install-dependencies\">2. Install dependencies<\/a><\/li>\n<li><a href=\"#3-set-up-environment-variables\">3. Set up environment variables<\/a><\/li>\n<li><a href=\"#4-run-the-project\">4. Run the project<\/a><\/li>\n<li><a href=\"#5-open-the-application\">5. Open the application<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#architecture\">Architecture<\/a><\/li>\n<li><a href=\"#technical-implementations\">Technical Implementations<\/a>\n<ul>\n<li><a href=\"#camera-captures\">Camera Captures<\/a><\/li>\n<li><a href=\"#image-prompt\">Image Prompt<\/a><\/li>\n<li><a href=\"#generate-video\">Generate Video<\/a><\/li>\n<li><a href=\"#kling-21-from-fal\">Kling 2.1 from Fal<\/a>\n<ul>\n<li><a href=\"#1-initiating-polling\">1. Initiating polling<\/a><\/li>\n<li><a href=\"#2-checking-the-job-status\">2. Checking the job status<\/a><\/li>\n<li><a href=\"#3-call-the-api-endpoint-for-a-status-check\">3. Call the API endpoint for a status check<\/a><\/li>\n<li><a href=\"#4-handling-video-when-the-status-is-complete\">4. Handling video when the status is complete<\/a>\n          <\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#api-routes\">API Routes<\/a><\/li>\n<li><a href=\"#saving-data-to-griddb\">Saving Data to GridDB<\/a><\/li>\n<li><a href=\"#read-data-from-griddb\">Read Data from GridDB<\/a><\/li>\n<li><a href=\"#user-interface\">User Interface<\/a><\/li>\n<\/ul>\n<h2 id=\"what-this-blog-is-about\">What This Blog is About<\/h2>\n<p>  Using your camera to capture memorable moments or interesting objects, then transforming them into creative, stylized<br \/>\n  video clips with AI, is an exciting way to engage users and demonstrate the power of modern technology.<\/p>\n<p>  In this guide, we build a developer-friendly pipeline using Next.js for the frontend, Fal.ai as a serverless inference<br \/>\n  runtime, Kling AI for video generation, and GridDB Cloud for real-time metadata storage. We\u2019ll walk through how to<br \/>\n  capture frames from the webcam, send them to Kling for enhancement, log metadata (image URL, applied effects, which is<br \/>\n  the prompt, and generated video URL) to GridDB, and render the final video.<\/p>\n<h2 id=\"prerequisites\">Prerequisites<\/h2>\n<h3 id=\"nodejs\">Node.js<\/h3>\n<p>  This project is built using Next.js, which requires Node.js version 16 or higher. You can download and install Node.js<br \/>\n  from <a href=\"https:\/\/nodejs.org\/en\">https:\/\/nodejs.org\/en<\/a>.<\/p>\n<h3 id=\"griddb\">GridDB<\/h3>\n<h4 id=\"sign-up-for-griddb-cloud-free-plan\">Sign Up for GridDB Cloud Free Plan<\/h4>\n<p>  If you would like to sign up for a GridDB Cloud Free instance, you can do so at the following link: <a\n    href=\"https:\/\/form.ict-toshiba.jp\/download_form_griddb_cloud_freeplan_e\">https:\/\/form.ict-toshiba.jp\/download_form_griddb_cloud_freeplan_e<\/a>.<\/p>\n<p>  After successfully signing up, you will receive a free instance along with the necessary details to access the GridDB<br \/>\n  Cloud Management GUI, including the <strong>GridDB Cloud Portal URL<\/strong>, <strong>Contract ID<\/strong>,<br \/>\n  <strong>Login<\/strong>, and <strong>Password<\/strong>.<\/p>\n<h4 id=\"griddb-webapi-url\">GridDB WebAPI URL<\/h4>\n<p>  Go to the GridDB Cloud Portal and copy the WebAPI URL from the <strong>Clusters<\/strong> section. It should look like<br \/>\n  this:<\/p>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-scaled.png\"><img fetchpriority=\"high\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-scaled.png\" alt=\"\" width=\"2560\" height=\"1265\" class=\"aligncenter size-full wp-image-55082\" srcset=\"\/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-scaled.png 2560w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-300x148.png 300w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-1024x506.png 1024w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-768x380.png 768w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-1536x759.png 1536w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-2048x1012.png 2048w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-portal-600x297.png 600w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/a><\/p>\n<h4 id=\"griddb-username-and-password\">GridDB Username and Password<\/h4>\n<p>  Go to the <strong>GridDB Users<\/strong> section of the GridDB Cloud portal and create or copy the username for<br \/>\n  <code>GRIDDB_USERNAME<\/code>. The password is set when the user is created for the first time. Use this as the<br \/>\n  <code>GRIDDB_PASSWORD<\/code>.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/griddb-cloud-users-scaled.png\"><img decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/griddb-cloud-users-scaled.png\" alt=\"\" width=\"2560\" height=\"1351\" class=\"aligncenter size-full wp-image-55083\" srcset=\"\/wp-content\/uploads\/2026\/03\/griddb-cloud-users-scaled.png 2560w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-users-300x158.png 300w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-users-1024x540.png 1024w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-users-768x405.png 768w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-users-1536x810.png 1536w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-users-2048x1080.png 2048w, \/wp-content\/uploads\/2026\/03\/griddb-cloud-users-600x317.png 600w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/a><\/p>\n<p>  For more details, to get started with GridDB Cloud, please follow this <a\n    href=\"https:\/\/griddb.net\/en\/blog\/griddb-cloud-quick-start-guide\/\">quick start guide<\/a>.<\/p>\n<h4 id=\"ip-whitelist\">IP Whitelist<\/h4>\n<p>  When running this project, please ensure that the IP address where the project is running is whitelisted. Failure to<br \/>\n  do so will result in a 403 status code or forbidden access.<\/p>\n<p>  You can use a website like <a href=\"https:\/\/whatismyipaddress.com\/\">What Is My IP Address<\/a> to find your public IP<br \/>\n  address.<\/p>\n<p>  To whitelist the IP, go to the GridDB Cloud Admin and navigate to the <strong>Network Access<\/strong> menu.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/ip-whitelist-scaled.png\"><img decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/ip-whitelist-scaled.png\" alt=\"\" width=\"2560\" height=\"1095\" class=\"aligncenter size-full wp-image-55084\" srcset=\"\/wp-content\/uploads\/2026\/03\/ip-whitelist-scaled.png 2560w, \/wp-content\/uploads\/2026\/03\/ip-whitelist-300x128.png 300w, \/wp-content\/uploads\/2026\/03\/ip-whitelist-1024x438.png 1024w, \/wp-content\/uploads\/2026\/03\/ip-whitelist-768x329.png 768w, \/wp-content\/uploads\/2026\/03\/ip-whitelist-1536x657.png 1536w, \/wp-content\/uploads\/2026\/03\/ip-whitelist-2048x876.png 2048w, \/wp-content\/uploads\/2026\/03\/ip-whitelist-600x257.png 600w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/a><\/p>\n<h3 id=\"fal-kling-21-api\">Fal Kling 2.1 API<\/h3>\n<p>  You need a Kling 2.1 API key to use this project. You can sign up for an account at <a\n    href=\"https:\/\/fal.ai\">fal.ai<\/a>.<\/p>\n<p>  After signing up, go to the <strong>Account<\/strong> section, and create and copy your API key.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-scaled.png\"><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-scaled.png\" alt=\"\" width=\"2560\" height=\"700\" class=\"aligncenter size-full wp-image-55080\" srcset=\"\/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-scaled.png 2560w, \/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-300x82.png 300w, \/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-1024x280.png 1024w, \/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-768x210.png 768w, \/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-1536x420.png 1536w, \/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-2048x560.png 2048w, \/wp-content\/uploads\/2026\/03\/fal-imagen-api-key-600x164.png 600w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/a><\/p>\n<p>  Kling\u202f2.1 is the latest version of Kling AI\u2019s text\/image-to-video generation engine, released in May 2025 by Kuaishou<br \/>\n  (also known as Kwai). It&#8217;s a significant upgrade from Kling 1.6 and 2.0, bringing smoother motion, sharper visuals,<br \/>\n  stronger prompt adherence, faster speeds, and better cost efficiency.<\/p>\n<h2 id=\"how-to-run\">How to Run<\/h2>\n<h3 id=\"1-clone-the-repository\">1. Clone the repository<\/h3>\n<p>  Clone the repository from <a href=\"https:\/\/github.com\/junwatu\/camtovid-ai\">https:\/\/github.com\/junwatu\/camtovid-ai<\/a><br \/>\n  to your local machine.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-sh\">$ git clone https:\/\/github.com\/junwatu\/camtovid-ai\n$ cd camtovid-ai\n$ cd apps<\/code><\/pre>\n<\/div>\n<h3 id=\"2-install-dependencies\">2. Install dependencies<\/h3>\n<p>  This project uses Bun for installing package dependencies. You can download Bun from <a\n    href=\"https:\/\/bun.sh\/\">https:\/\/bun.sh<\/a>.<\/p>\n<div class=\"clipboard\">\n<pre><code>$ bun install<\/code><\/pre>\n<\/div>\n<h3 id=\"3-set-up-environment-variables\">3. Set up environment variables<\/h3>\n<p>  Copy file <code>.env.example<\/code> to <code>.env<\/code> and fill in the values:<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-ini\"># Copy this file to .env.local and add your actual API keys\n# Never commit .env.local to version control\n\n# Fal.ai API Key for Kling 2.1\n# Get your key from: https:\/\/fal.ai\/dashboard\nFAL_KEY=\n\n\nGRIDDB_WEBAPI_URL=\nGRIDDB_PASSWORD=\nGRIDDB_USERNAME=<\/code><\/pre>\n<\/div>\n<p>  Please look at the section on <a href=\"#prerequisites\">Prerequisites<\/a> before running the project.<\/p>\n<h3 id=\"4-run-the-project\">4. Run the project<\/h3>\n<p>  Run the project using the following command:<\/p>\n<div class=\"clipboard\">\n<pre><code>$ npm run dev<\/code><\/pre>\n<\/div>\n<h3 id=\"5-open-the-application\">5. Open the application<\/h3>\n<p>  Open the application in your browser at <a href=\"http:\/\/localhost:3000\">http:\/\/localhost:3000<\/a>. You also need to<br \/>\n  allow the browser to access your camera. If you access the web application from a mobile device, there will be an<br \/>\n  option to select between the rear and back camera.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/allow-camera-scaled.png\"><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/allow-camera-scaled.png\" alt=\"\" width=\"2560\" height=\"1529\" class=\"aligncenter size-full wp-image-55075\" srcset=\"\/wp-content\/uploads\/2026\/03\/allow-camera-scaled.png 2560w, \/wp-content\/uploads\/2026\/03\/allow-camera-300x179.png 300w, \/wp-content\/uploads\/2026\/03\/allow-camera-1024x611.png 1024w, \/wp-content\/uploads\/2026\/03\/allow-camera-768x459.png 768w, \/wp-content\/uploads\/2026\/03\/allow-camera-1536x917.png 1536w, \/wp-content\/uploads\/2026\/03\/allow-camera-2048x1223.png 2048w, \/wp-content\/uploads\/2026\/03\/allow-camera-600x358.png 600w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/a><\/p>\n<h2 id=\"architecture\">Architecture<\/h2>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/arch.png\"><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/arch.png\" alt=\"\" width=\"2352\" height=\"1645\" class=\"aligncenter size-full wp-image-55077\" srcset=\"\/wp-content\/uploads\/2026\/03\/arch.png 2352w, \/wp-content\/uploads\/2026\/03\/arch-300x210.png 300w, \/wp-content\/uploads\/2026\/03\/arch-1024x716.png 1024w, \/wp-content\/uploads\/2026\/03\/arch-768x537.png 768w, \/wp-content\/uploads\/2026\/03\/arch-1536x1074.png 1536w, \/wp-content\/uploads\/2026\/03\/arch-2048x1432.png 2048w, \/wp-content\/uploads\/2026\/03\/arch-600x420.png 600w\" sizes=\"(max-width: 2352px) 100vw, 2352px\" \/><\/a><\/p>\n<p>  The architecture and user flow are intentionally simple for rapid development and ease of use. Users access the app<br \/>\n  from any desktop or mobile browser. After capturing an image with their device\u2019s camera, they enter a prompt<br \/>\n  describing the video they want to generate.<\/p>\n<p>  The Next.js frontend sends both the image and prompt to Fal AI\u2019s Kling 2.1 model. Kling 2.1 processes these inputs,<br \/>\n  generates a video, and returns it directly to the browser client.<\/p>\n<p>  When generation is done, the metadata: image URL, prompt, and generated video URL will be saved to the GridDB Cloud.<\/p>\n<h2 id=\"technical-implementations\">Technical Implementations<\/h2>\n<h3 id=\"camera-captures\">Camera Captures<\/h3>\n<p>  The <code>use-camera.ts<\/code> is a <code>useCamera<\/code> custom hook, which encapsulates all the logic for<br \/>\n  controlling the camera, including starting, stopping, switching, and capturing a photo. The <code>capturePhoto<\/code><br \/>\n  function is the one that actually captures the image from the video stream and returns it as a base64-encoded JPEG.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">\/\/ ... existing code ...\n  const capturePhoto = useCallback(() =&gt; {\n    if (videoRef.current &amp;&amp; canvasRef.current) {\n      const canvas = canvasRef.current\n      const video = videoRef.current\n      const context = canvas.getContext(&#x27;2d&#x27;)\n\n      canvas.width = video.videoWidth\n      canvas.height = video.videoHeight\n\n      if (context) {\n        context.drawImage(video, 0, 0)\n        const imageData = canvas.toDataURL(&#x27;image\/jpeg&#x27;)\n        stopCamera()\n        optionsRef.current.onSuccess?.(&#x27;Photo captured successfully&#x27;)\n        return imageData\n }\n }\n    optionsRef.current.onError?.(&#x27;Failed to capture photo&#x27;)\n    return null\n }, [stopCamera])\n\/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  The <code>app\/page.tsx<\/code> is the main page component. It uses the <code>useCamera<\/code> hook to get the<br \/>\n  <code>capturePhoto<\/code> function and other camera-related state and methods. The <code>handleCapturePhoto<\/code><br \/>\n  function is called when the user clicks the capture button. This function calls <code>capturePhoto<\/code> from the<br \/>\n  hook and then updates the application state with the captured image data.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">\/\/ ... existing code ...\n  \/\/ Handle photo capture\n  const handleCapturePhoto = () =&gt; {\n    const imageData = capturePhoto()\n    if (imageData) {\n      setCapturedImage(imageData)\n      setState(&#x27;captured&#x27;)\n }\n }\n\/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<h3 id=\"image-prompt\">Image Prompt<\/h3>\n<p>  In the <code>app\/page.tsx<\/code>, there is a prompt input that only shows after a photo has been captured<br \/>\n  <code>(state === 'captured')<\/code>.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">\/\/ ... existing code ...\n {\/* Prompt Input Section - Only show after photo is captured *\/}\n {(state === &quot;captured&quot; || state === &quot;generating&quot; || state === &quot;completed&quot;) &amp;&amp; (\n &lt;div&gt;\n &lt;div className=&quot;space-y-2&quot;&gt;\n &lt;Label htmlFor=&quot;prompt&quot; className=&quot;text-lg font-semibold&quot;&gt;\n                        2. Enter Your Creative Prompt\n &lt;\/Label&gt;\n &lt;Textarea\n                        id=&quot;prompt&quot;\n                        placeholder=&quot;e.g., a majestic lion roaring on a cliff, cinematic lighting&quot;\n                        value={prompt}\n                        onChange={(e) =&gt; setPrompt(e.target.value)}\n                        className=&quot;min-h-[80px] text-base&quot;\n                        disabled={state === &quot;generating&quot;}\n \/&gt;\n &lt;\/div&gt;\n &lt;\/div&gt;\n )}\n &lt;\/TabsContent&gt;\n\n &lt;TabsContent value=&quot;video&quot; className=&quot;p-6&quot;&gt;\n &lt;div className=&quot;space-y-6&quot;&gt;\n {\/* Video and Generation status *\/}\n\/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  Along with the <code>capturedImage<\/code>, this <code>prompt<\/code> will be used to generate video. This will happen<br \/>\n  if the user clicks the <code>Generate Video<\/code> button.<\/p>\n<h3 id=\"generate-video\">Generate Video<\/h3>\n<p>  Kling 2.1 API from the Fal needs two main parameters:<\/p>\n<ol>\n<li><strong>Image reference<\/strong>, which is the image the user captured from the camera<\/li>\n<li><strong>Prompt<\/strong> for the video creation.<\/li>\n<\/ol>\n<p>  Before the video generation, the captured image needed to be saved first. In this app, we use Fal server to save the<br \/>\n  captured image.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">    \/\/ ... existing code ...\n      static async uploadImage(imageData: string): Promise&lt;ImageUploadResponse&gt; {\n try {\n          \/\/ Convert data URL to blob\n          const response = await fetch(imageData);\n          const blob = await response.blob();\n          \n          \/\/ Create form data\n          const formData = new FormData();\n          formData.append(&#x27;file&#x27;, blob, &#x27;captured-image.jpg&#x27;);\n    \n          const uploadResponse = await fetch(`${this.baseUrl}\/upload-image`, {\n            method: &#x27;POST&#x27;,\n            body: formData,\n });\n    \/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  The upload process is handled by the <code>\/api\/upload-image<\/code> endpoint. This route uses the Fal.ai client<br \/>\n  <code>(@fal-ai\/client)<\/code> to upload it to Fal.ai&#8217;s storage. It then returns the public URL of the uploaded image.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">    \/\/ ... existing code ...\n    import { fal } from &#x27;@fal-ai\/client&#x27;;\n    \/\/ ... existing code ...\n        \/\/ Upload file to Fal.ai storage\n        const uploadUrl = await fal.storage.upload(file);\n    \n        return NextResponse.json({\n          success: true,\n          url: uploadUrl,\n          file_name: file.name\n });\n    \/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  After successfully uploading an image and we get the image URL, the <code>useVideoGeneration<\/code> hook will call<br \/>\n  <code>VideoSeevice.generateVideo<\/code>, passing the image URL and the user&#8217;s <code>prompt<\/code>:<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">    \/\/ ... existing code ...\n          setUploadedImageUrl(uploadResult.url)\n    \n          \/\/ Start video generation\n          setState(&#x27;generating&#x27;)\n          setGenerationStatus(&#x27;initializing&#x27;)\n          \n          const result = await VideoService.generateVideo({\n            image_url: uploadResult.url,\n            prompt: prompt,\n })\n    \/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  The <code>generateVideo<\/code> will call the <code>\/api\/generate-video<\/code> endpoint, which in turn calls the Fal.ai<br \/>\n  Kling AI model to start the video generation job.<\/p>\n<h3 id=\"kling-21-from-fal\">Kling 2.1 from Fal<\/h3>\n<p>  The AI model we use to generate video is <a\n    href=\"https:\/\/fal.ai\/models\/fal-ai\/kling-video\/v2.1\/pro\/image-to-video\">Kling 2.1<\/a>. Like other models on Fal, it<br \/>\n  is best accessed asynchronously. After the video generation job is submitted to Fal.ai, the application enters a<br \/>\n  monitoring phase to wait for the video to be ready. This is handled by polling for the result in an asynchronous<br \/>\n  process.<\/p>\n<p>  Here, step by step, is implemented in this app until the video is ready:<\/p>\n<h4 id=\"1-initiating-polling\">1. Initiating polling.<\/h4>\n<p>  The <code>useVideoGeneration<\/code> hook in <code>hooks\/use-video-generation.ts<\/code> doesn&#8217;t just fire and forget.<br \/>\n  After submitting the job and getting a <code>request_id<\/code>, it starts a polling mechanism to repeatedly check the<br \/>\n  status of the generation job.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">        \/\/ ... existing code ...\n      if (result.success &amp;&amp; result.request_id) {\n        const imageUrl = uploadResult.url\n        \n        \/\/ Polling function\n        const poll = async () =&gt; {\n          try {\n            const videoResult = await VideoService.getVideoResult(result.request_id!)\n            const status = (videoResult as any).status\n            \n            setGenerationStatus(status)\n            options.onStatusChange?.(status)\n\n            if (status === &#x27;COMPLETED&#x27;) {\n            \/\/ ... existing code ...\n     } else if (status === &#x27;FAILED&#x27; || status === &#x27;CANCELLED&#x27;) {\n            \/\/ ... existing code ...\n     } else {\n              \/\/ Continue polling\n              setTimeout(poll, pollInterval)\n     }\n     } catch (error) {\n          \/\/ ... existing code ...\n     }\n     }\n        \n        poll()\n     }\n    \/\/ ... existing code ...\n <\/code><\/pre>\n<\/div>\n<h4 id=\"2-checking-the-job-status\">2. Checking the job status.<\/h4>\n<p>  The poll function calls <code>VideoService.getVideoResult<\/code>, which is responsible for fetching the latest status<br \/>\n  of the video generation job.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">        \/\/ ... existing code ...\n      \/**\n     * Get the result of a video generation task\n     *\/\n      static async getVideoResult(requestId: string): Promise&lt;VideoResultResponse&gt; {\n     try {\n          const response = await fetch(`${this.baseUrl}\/get-video?request_id=${requestId}`);\n          const result = await response.json();\n    \n          if (!response.ok) {\n            throw new Error(result.error || &#x27;Failed to get video result&#x27;);\n     }\n    \n          return result;\n     } catch (error) {\n     return {\n            success: false,\n            error: &#x27;Failed to process request&#x27;,\n            details: error instanceof Error ? error.message : &#x27;Unknown error&#x27;\n     };\n     }\n     }\n    \/\/ ... existing code ...\n <\/code><\/pre>\n<\/div>\n<h4 id=\"3-call-the-api-endpoint-for-a-status-check\">3. Call the API endpoint for a status check.<\/h4>\n<p>  The <code>VideoService<\/code> calls the <code>\/api\/get-video<\/code> endpoint. This endpoint uses the<br \/>\n  <code>fal-ai<\/code> client library to get the status of the job from Fal.ai using the <code>request_id<\/code>.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">         \/\/ ... existing code ...\n    export async function GET(request: NextRequest) {\n      try {\n        const { searchParams } = new URL(request.url)\n        const requestId = searchParams.get(&#x27;request_id&#x27;)\n    \n        if (!requestId) {\n          return NextResponse.json(\n     { error: &#x27;Missing request_id parameter&#x27; },\n     { status: 400 }\n     );\n     }\n    \n        const result = await fal.queue.get(requestId);\n    \n        return NextResponse.json(result);\n    \n     } catch (error) {\n        return NextResponse.json(\n    \/\/ ... existing code ...\n <\/code><\/pre>\n<\/div>\n<h4 id=\"4-handling-video-when-the-status-is-complete\">4. Handling video when the status is complete.<\/h4>\n<p>  Once the polling mechanism receives a <code>COMPLETED<\/code> status, the <code>useVideoGeneration<\/code> hook updates<br \/>\n  the application state with the generated video&#8217;s URL and calls the <code>onSuccess<\/code> callback that was passed to<br \/>\n  it from the main page component.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">        \/\/ ... existing code ...\n            if (status === &#x27;COMPLETED&#x27;) {\n              const generatedVideoUrl = (videoResult as any).data.data.video.url\n              setGeneratedVideo(generatedVideoUrl)\n              setState(&#x27;completed&#x27;)\n              setIsLoading(false)\n              setGenerationStatus(null)\n              \n              options.onSuccess?.(generatedVideoUrl, imageUrl, prompt)\n              \n     }\n    \/\/ ... existing code ...\n <\/code><\/pre>\n<\/div>\n<p>  Once the video generation is complete, the video will be displayed in the <strong>Generated Video<\/strong> UI tab.<br \/>\n  More on this in the <a href=\"#user-interface\">User Interface<\/a> section.<\/p>\n<h2 id=\"api-routes\">API Routes<\/h2>\n<p>  This web app exposed some API. Here is a table summarizing all the API routes used in this web application, along with<br \/>\n  their HTTP methods and descriptions.<\/p>\n<table class=\"markdown-table\" style=\"border-collapse: collapse; width: 100%; margin: 1em 0;\">\n<thead>\n<tr>\n<th style=\"border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; text-align: left;\">Route<\/th>\n<th style=\"border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; text-align: left;\">HTTP Method<\/th>\n<th style=\"border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; text-align: left;\">Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>\/api\/upload-image<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>POST<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\">Receives an image file from the client and<br \/>\n        uploads it to Fal.ai&#8217;s temporary storage, returning a URL.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>\/api\/generate-video<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>POST<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\">Submits a job to the Fal.ai Kling AI model to<br \/>\n        generate a video using an image URL and a text prompt.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>\/api\/get-video<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>GET<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\">Polls the Fal.ai service to check the status<br \/>\n        of a video generation job using its <code>request_id<\/code>.<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>\/api\/save-data<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\"><code>POST<\/code><\/td>\n<td style=\"border: 1px solid #ddd; padding: 8px; text-align: left;\">Saves the metadata for a generated video<br \/>\n        (image URL, prompt, video URL) into the GridDB database.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"saving-data-to-griddb\">Saving Data to GridDB<\/h2>\n<p>  After the video generation is completed, the app will save the metadata to GridDB Cloud. This saves the metadata of<br \/>\n  the generated video (the original image URL from Fal.ai, the user&#8217;s prompt, and the new video URL) to your GridDB<br \/>\n  database.<\/p>\n<p>  So, here is the data schema used in the database that you can find in the <code>lib\/types\/griddb.types.ts<\/code> file:<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">\/\/ Types for container data\nexport interface GridDBData {\n    id: string | number;\n    imageURL: string;\n    prompt: string;\n    generatedVideoURL: string;\n}<\/code><\/pre>\n<\/div>\n<p>  The sava data happening in the <code>app\\page.tsx<\/code> main component:<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">    \/\/ ... existing code ...\n      \/\/ Video generation management\n      const {\n    \/\/ ... existing code ...\n } = useVideoGeneration({\n        onSuccess: async (videoUrl, imageUrl, promptText) =&gt; {\n          setState(&#x27;completed&#x27;)\n          setActiveTab(&#x27;video&#x27;)\n          \n    \/\/ ... existing code ...\n    \n          \/\/ Auto-save data\n          const saved = await saveData({\n            imageURL: imageUrl,\n            prompt: promptText,\n            generatedVideoURL: videoUrl,\n })\n    \/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  This <code>saveData<\/code> function calls the <code>\/api\/save-data<\/code> endpoint to perform the database operation.<\/p>\n<h2 id=\"read-data-from-griddb\">Read Data from GridDB<\/h2>\n<p>  The <code>GET<\/code> method in the <code>\/api\/save-data\/route.ts<\/code> file is responsible for fetching all records<br \/>\n  from the database.<\/p>\n<p>  Here&#8217;s how it works:<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-javascript\">\/\/ ... existing code ...\n\/\/ Optional: Add GET method to retrieve data\nexport async function GET(request: NextRequest) {\n  try {\n    const { searchParams } = new URL(request.url);\n    const id = searchParams.get(&#x27;id&#x27;);\n    const limit = searchParams.get(&#x27;limit&#x27;) || &#x27;10&#x27;;\n\/\/ ... existing code ...\n    let query;\n    if (id) {\n      \/\/ Search for specific ID\n      query = {\n        type: &#x27;sql-select&#x27;,\n        stmt: `SELECT * FROM camvidai WHERE id = ${parseInt(id)}`\n };\n } else {\n      \/\/ Get recent entries\n      query = {\n        type: &#x27;sql-select&#x27;,\n        stmt: `SELECT * FROM camvidai ORDER BY id DESC LIMIT ${parseInt(limit)}`\n };\n }\n\n    const result = await dbClient.searchData([query]);\n\/\/ ... existing code ...<\/code><\/pre>\n<\/div>\n<p>  This <code>GET<\/code> function handles two cases:<\/p>\n<ol>\n<li> <strong>Fetch by ID:<\/strong> If an <code>id<\/code> is provided as a query parameter (e.g.,<br \/>\n    <code>\/api\/save-data?id=123<\/code>), it fetches that specific record.<\/li>\n<li> <strong>Fetch All (Recent):<\/strong> If no <code>id<\/code> is provided, it fetches the most recent entries from<br \/>\n    the <code>camvidai<\/code> container, ordering them by ID in descending order. It defaults to a <code>limit<\/code> of<br \/>\n    10 records, but this can be changed with a query parameter (e.g., <code>\/api\/save-data?limit=50<\/code>).<\/li>\n<\/ol>\n<p>  So, to get all the data (or at least the most recent set), you would make a <code>GET<\/code> request to<br \/>\n  <code>\/api\/save-data<\/code>.<\/p>\n<h2 id=\"user-interface\">User Interface<\/h2>\n<p>  The user interface is built using Next.js. It has three main views:<\/p>\n<ol>\n<li>Capture Tab.<\/li>\n<li>Prompt Input (shows only after image captured).<\/li>\n<li>Generated Video Tab.<\/li>\n<\/ol>\n<p>  In essence, the UI is a wizard-like workflow that moves the user through a linear sequence: <code>Capture<\/code> -><br \/>\n  <code>Prompt<\/code> -> <code>Generate<\/code> -> <code>View Video<\/code>.<\/p>\n<p>  The best way to get to know the user interface is to try it yourself! \u263a\ufe0f However, here is a demo so you can see what<br \/>\n  the app looks like.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2026\/03\/demo.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/03\/demo.gif\" alt=\"\" width=\"1124\" height=\"720\" class=\"aligncenter size-full wp-image-55079\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment variables 4. Run the project 5. Open the application Architecture Technical Implementations Camera Captures Image Prompt Generate Video Kling 2.1 from Fal 1. Initiating polling 2. Checking the job status 3. [&hellip;]<\/p>\n","protected":false},"author":41,"featured_media":55078,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[121],"tags":[],"class_list":["post-55088","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Generate Fun AI Videos from a Photo with Kling | GridDB: Open Source Time Series Database for IoT<\/title>\n<meta name=\"description\" content=\"Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Generate Fun AI Videos from a Photo with Kling | GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"og:description\" content=\"Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment\" \/>\n<meta property=\"og:url\" content=\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\" \/>\n<meta property=\"og:site_name\" content=\"GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/griddbcommunity\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-26T19:28:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.griddb.net\/wp-content\/uploads\/2026\/03\/cover.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1536\" \/>\n\t<meta property=\"og:image:height\" content=\"1024\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"griddb-admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:site\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"griddb-admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\"},\"author\":{\"name\":\"griddb-admin\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\"},\"headline\":\"Generate Fun AI Videos from a Photo with Kling\",\"datePublished\":\"2026-03-26T19:28:53+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\"},\"wordCount\":1582,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2026\/03\/cover.png\",\"articleSection\":[\"Blog\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\",\"url\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\",\"name\":\"Generate Fun AI Videos from a Photo with Kling | GridDB: Open Source Time Series Database for IoT\",\"isPartOf\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2026\/03\/cover.png\",\"datePublished\":\"2026-03-26T19:28:53+00:00\",\"description\":\"Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage\",\"url\":\"\/wp-content\/uploads\/2026\/03\/cover.png\",\"contentUrl\":\"\/wp-content\/uploads\/2026\/03\/cover.png\",\"width\":1536,\"height\":1024},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.griddb.net\/en\/#website\",\"url\":\"https:\/\/www.griddb.net\/en\/\",\"name\":\"GridDB: Open Source Time Series Database for IoT\",\"description\":\"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL\",\"publisher\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.griddb.net\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.griddb.net\/en\/#organization\",\"name\":\"Fixstars\",\"url\":\"https:\/\/www.griddb.net\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"contentUrl\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"width\":200,\"height\":83,\"caption\":\"Fixstars\"},\"image\":{\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/griddbcommunity\/\",\"https:\/\/x.com\/GridDBCommunity\",\"https:\/\/www.linkedin.com\/company\/griddb-by-toshiba\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\",\"name\":\"griddb-admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.griddb.net\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"caption\":\"griddb-admin\"},\"url\":\"https:\/\/www.griddb.net\/en\/author\/griddb-admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Generate Fun AI Videos from a Photo with Kling | GridDB: Open Source Time Series Database for IoT","description":"Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment","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:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/","og_locale":"en_US","og_type":"article","og_title":"Generate Fun AI Videos from a Photo with Kling | GridDB: Open Source Time Series Database for IoT","og_description":"Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment","og_url":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/","og_site_name":"GridDB: Open Source Time Series Database for IoT","article_publisher":"https:\/\/www.facebook.com\/griddbcommunity\/","article_published_time":"2026-03-26T19:28:53+00:00","og_image":[{"width":1536,"height":1024,"url":"https:\/\/www.griddb.net\/wp-content\/uploads\/2026\/03\/cover.png","type":"image\/png"}],"author":"griddb-admin","twitter_card":"summary_large_image","twitter_creator":"@GridDBCommunity","twitter_site":"@GridDBCommunity","twitter_misc":{"Written by":"griddb-admin","Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#article","isPartOf":{"@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/"},"author":{"name":"griddb-admin","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233"},"headline":"Generate Fun AI Videos from a Photo with Kling","datePublished":"2026-03-26T19:28:53+00:00","mainEntityOfPage":{"@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/"},"wordCount":1582,"commentCount":0,"publisher":{"@id":"https:\/\/www.griddb.net\/en\/#organization"},"image":{"@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2026\/03\/cover.png","articleSection":["Blog"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/","url":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/","name":"Generate Fun AI Videos from a Photo with Kling | GridDB: Open Source Time Series Database for IoT","isPartOf":{"@id":"https:\/\/www.griddb.net\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage"},"image":{"@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2026\/03\/cover.png","datePublished":"2026-03-26T19:28:53+00:00","description":"Table of Contents Prerequisites Node.js GridDB Fal Kling 2.1 API How to Run 1. Clone the repository 2. Install dependencies 3. Set up environment","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/griddb.net\/en\/blog\/generate-fun-ai-videos-from-a-photo-with-kling\/#primaryimage","url":"\/wp-content\/uploads\/2026\/03\/cover.png","contentUrl":"\/wp-content\/uploads\/2026\/03\/cover.png","width":1536,"height":1024},{"@type":"WebSite","@id":"https:\/\/www.griddb.net\/en\/#website","url":"https:\/\/www.griddb.net\/en\/","name":"GridDB: Open Source Time Series Database for IoT","description":"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL","publisher":{"@id":"https:\/\/www.griddb.net\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.griddb.net\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.griddb.net\/en\/#organization","name":"Fixstars","url":"https:\/\/www.griddb.net\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/","url":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","contentUrl":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","width":200,"height":83,"caption":"Fixstars"},"image":{"@id":"https:\/\/www.griddb.net\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/griddbcommunity\/","https:\/\/x.com\/GridDBCommunity","https:\/\/www.linkedin.com\/company\/griddb-by-toshiba"]},{"@type":"Person","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233","name":"griddb-admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.griddb.net\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","caption":"griddb-admin"},"url":"https:\/\/www.griddb.net\/en\/author\/griddb-admin\/"}]}},"_links":{"self":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts\/55088","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/comments?post=55088"}],"version-history":[{"count":3,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts\/55088\/revisions"}],"predecessor-version":[{"id":55091,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/posts\/55088\/revisions\/55091"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/media\/55078"}],"wp:attachment":[{"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/media?parent=55088"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/categories?post=55088"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.griddb.net\/en\/wp-json\/wp\/v2\/tags?post=55088"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}