如何使用 Gemini API 构建视频字幕生成器

译文 精选
人工智能
在本教程中,你将使用 Google 的 Gemini API 构建人工智能驱动的字幕生成器。我们将创建一个名为“AI-Subtitle-Generator”的项目,该项目的前端使用 React,后端使用 Express。准备好了吗?让我们马上出发吧!

译者 | 崔皓

审校 | 重楼

开篇

在本教程中,将使用 Google 的 Gemini API 构建人工智能驱动的字幕生成器。我们将创建一个名为“AI-Subtitle-Generator”的项目,该项目的前端使用 React,后端使用 Express。准备好了吗?让我们马上出发吧!

先决条件

构建项目之前你需要对React 和 Express 有基本了解

Gemini API 是什么?

Google 的 Gemini API 是一款功能强大的工具,可让将高级 AI 功能集成到的应用程序中。 Gemini 是多模态模型,它支持用户使用各种类型的输入,例如文本、图像、音频和视频

它擅长分析和处理大量文本,特别是从视频中提取信息的能力,非常适合字幕生成器的项目

如何获取 API 密钥

API 密钥充当唯一标识符并验证对服务的请求。它对于访问和使用 Gemini AI 的功能至关重要。这个密钥将允许我们的应用程序与 Gemini 进行通信,也是构建该项目的关键因素。

如下图所示,进入Google AI Studio ,然后点击“获取 API 密钥”(Get API key)

在打开的API KEY 页面,单击“创建 API 密钥”:

该操作将创建一个新的 API 密钥,并复制密钥对其进行妥善保存

这个就是访问Gemini API 密钥。此密钥用于验证应用程序对 Gemini API 的请求。每次应用程序向 Gemini 发送请求时,都必须包含此密钥。 Gemini 使用此密钥来验证请求是否来自授权来源。如果没有此 API 密钥,请求将被拒绝,同时应用也无法访问 Gemini 的服务。

项目设置

首先,基于项目创建一个新文件夹。我们称之为ai-subtitle-generator 。

在ai-subtitle-generator文件夹内,创建两个子文件夹: client和server 。 client文件夹将包含 React 前端, server文件夹将包含 Express 后端。

前端设置

我们先将重点关注放到前端并设置一个基本的 React 应用程序。

通过如下命令,导航到client文件夹:

cd client

使用Vite创建一个新的React项目。执行如下命令:

npm create vite@latest .

根据提示时,选择“React“React + TS”或者“React + JS”。在本教程中,将使用 React + TS。当然你也可以根据喜好选择其他的选项。

接下来,使用以下命令安装依赖项:

npm install

然后启动开发服务器:

npm run dev

在前端处理文件上传

现在在client/src/App.tsx中,添加以下代码:

//  client/src/App.tsx
const App = () => {
    const handleSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
    e.preventDefault();
    try {
      const formData = new FormData(e.currentTarget);
      console.log(formData)
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input type="file" accept="video/*,.mkv" name="video" />
        <input type="submit" />
      </form>
    </div>
  );
};
export default App;

在上面的代码中,我们使用了一个输入标签来接受视频并将其命名为video 。该名称将附加到FormData对象中。

在将视频发送到服务器的过程中,使用到了“键值对“的发送方式,其中是video ,值是文件数据。

为什么是键值对?因为当服务器收到请求时,它需要解析传入的块。解析后,视频数据将在req.files[key]中使用,其中key是前端分配的名称(在本例中为video )。

这就是为什么使用FormData对象的原因。当创建一个新的FormData实例并将e.target传递给它时,所有表单字段及其名称将自动生成键值对的形式

服务器设置

目前为止,我们已经获取了API 密钥,接着需要设置后端服务器。该服务器将处理来自前端上传视频,并与 Gemini API 进行通信从而生成字幕。

通过如下命令,导航到server文件夹:

cd server

并初始化项目:

npm init -y

然后安装必要的包:

npm install express dotenv cors @google/generative-ai express-fileupload nodemon

这些是在此项目中使用的后端依赖项:

  • express 用于创建后端 API 的 Web 框架。
  • dotenv 从.env文件加载环境变量。
  • cors 启用跨源资源共享,允许前端与后端进行通信。
  • @google/generative-ai 用于与 Gemini API 交互的 Google AI 库。
  • express-fileupload 处理文件上传,可以轻松访问服务器上上传的文件。
  • nodemon 更改代码时自动重新启动服务器。

设置环境变量

现在,创建一个名为.env的文件。可以在此处管理 API 密钥。

//.env
API_KEY = YOUR_API_API
PORT = 3000

更新package.json

对于项目,我们使用 ES6 模块而不是 CommonJS。要启用此功能,请使用以下代码更新的package.json文件:

{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",       //Add "type": "module" to enable ES6 modules
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"    //configure nodemon
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@google/generative-ai": "^0.21.0",
    "cors": "^2.8.5",
    "dotenv": "^16.4.7",
    "express": "^4.21.1",
    "express-fileupload": "^1.5.1",
    "nodemon": "^3.1.7"
  }
}

Express 的基本设置

创建文件server.js 。现在,让我们设置一个基本的 Express 应用程序。

//  server/server.js

import express from "express";
import { configDotenv } from "dotenv";
import fileUpload from "express-fileupload";
import cors from "cors"

const app = express();

configDotenv();           //configure the env
app.use(fileUpload());    //it will parse the mutipart data
app.use(express.json());  // Enable JSON parsing for request bodies
app.use(cors())           //configure cors

app.use("/api/subs",subRoutes);  // Use routes for the "/api/subs" endpoint

app.listen(process.env.PORT, () => {   //access the PORT from the .env
  console.log("server started");         
});

在代码中,我们创建一个 Express 应用程序实例,然后加载环境变量。该环境变量可以用来保存API 密钥等敏感数据。接下来,利用中间件: fileUpload准备服务器来接收上传的视频, express.json允许接收 JSON 数据, cors允许前端和后端之间的通信。

接着,定义路由(/api/subs)来处理与字幕生成相关的所有请求。路由的具体逻辑将在subs.routes.js中体现。最后,启动服务器,告诉它监听.env文件中定义的端口,从而保证请求响应

然后,创建一系列文件夹来管理代码。当然,也可以在单个文件中管理代码,但将其构建到单独的文件夹进行管理更加方便、更加清晰

下面是服务器的最终文件夹结构:

server/

├── server.js

├── controller/

│ └── subs.controller.js

├── gemini/

│ ├── gemini.config.js

├── routes/

│ └── subs.routes.js

├── uploads/

├── utils/

│ ├── fileUpload.js

│ └── genContent.js

└── .env

注意:现在不必担心创建此文件夹结构。这仅供参考。跟着文章一步步来,在后面的内容中会逐步搭建这个结构。

创建路由

创建一个routes文件夹,然后创建subs.routes.js文件如下

// server/routes/sub.routes.js
import express from "express"
import { uploadFile } from "../controller/subs.controller.js"    // import the uploadFile function from the controller folder

const router = express.Router()

router.post("/",uploadFile)    // define a POST route that calls the uploadFile function

export default router     // export the router to use in the main server.js file

此代码定义了服务器的路由,特别是处理视频上传和字幕生成的路由。

使用express.Router()创建一个新的路由器实例。这使我们能够定义新的路由,该路由可以独立于主服务器路由,从而改进代码组织结构,使之更加清晰。在 API 接入点的根路径("/")处定义 POST 路由。当对此路由发出 POST 请求时(当用户在前端提交视频上传表单时会发生),将调用uploadFile函数。该函数将处理实际的上传和字幕生成。

最后,我们导出路由器,以便可以在主服务器文件(server.js)中使用它来将此路由连接到主应用程序。

配置Gemini

接下来的任务就是配置应用程序如何与 Gemini 交互。

创建一个gemini文件夹,然后创建一个名为gemini.config.js的新文件:

//  server/gemini/gemini.config.js

import {
  GoogleGenerativeAI,
  HarmBlockThreshold,
  HarmCategory,
} from "@google/generative-ai";
import { configDotenv } from "dotenv";
configDotenv();

const genAI = new GoogleGenerativeAI(process.env.API_KEY);  // Initialize Google Generative AI with the API key

const safetySettings = [
  {
    category: HarmCategory.HARM_CATEGORY_HARASSMENT,
    threshold: HarmBlockThreshold.BLOCK_NONE,
  },
  {
    category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
    threshold: HarmBlockThreshold.BLOCK_NONE,
  },
  {
    category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
    threshold: HarmBlockThreshold.BLOCK_NONE,
  },
  {
    category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
    threshold: HarmBlockThreshold.BLOCK_NONE,
  },
];

const model = genAI.getGenerativeModel({
  model: "gemini-1.5-flash-001",    //choose the model
  safetySettings: safetySettings,   //optional safety settings
});
export default model;    //export the model

在上面的代码中, safetySettings是可选的。这些设置允许定义 Gemini 输出中潜在有害内容(例如仇恨言论、暴力或露骨内容)的阈值。

创建一个控制器来处理端点逻辑

创建一个controller文件夹,并在其中创建一个名为subs.controller.js的文件。在此文件中,将处理与 Gemini 模型交互的端点逻辑。

在 server/controller/subs.controller.js ,添加这段代码:

// server/controller/subs.controller.js

import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";

const __filename = fileURLToPath(import.meta.url);  //converts the module URL to a file path
const __dirname = path.dirname(__filename);   //get the current file directory

export const uploadFile = async (req, res) => {
  try {
    if (!req.files || !req.files.video) {   //if there is no file available, return error to the client
      return res.status(400).json({ error: "No video uploaded" });
    }

    const videoFile = req.files.video;   //access the video
    const uploadDir = path.join(__dirname, "..", "uploads");   //path to upload the video temporarily

    if (!fs.existsSync(uploadDir)) {   //check if the directory exists
      fs.mkdirSync(uploadDir);      //if not create a new one
    }

    const uploadPath = path.join(uploadDir, videoFile.name);  

    await videoFile.mv(uploadPath);  //it moves the video from the buffer to the "upload" folder

    return res.status(200).json({ message:"file uploaded sucessfully" });
  } catch (error) {
    return res
      .status(500)
      .json({ error: "Internal server error: " + error.message });
  }
};

由于我们使用的是 ES6 模块,因此__dirname默认情况下不可用。与 CommonJS 相比,文件处理机制有所不同。因此,将使用fileURLToPath来处理文件路径。

将文件从默认的临时位置(缓冲区)移动到uploads夹。

但文件上传过程尚未完成。我们仍然需要将文件发送到Google AI文件管理器,上传后,它会返回一个URI,模型会使用这个URI进行视频分析。

如何将文件上传到 Google AI 文件管理器

创建文件夹utils并创建文件fileUpload.js 。可以参考上面提供的文件夹结构。

//  server/utils/fileUpload.js

import { GoogleAIFileManager, FileState } from "@google/generative-ai/server";
import { configDotenv } from "dotenv";
configDotenv();

export const fileManager = new GoogleAIFileManager(process.env.API_KEY);  //create a new GoogleAIFileManager instance

export async function fileUpload(path, videoData) {  
  try {
    const uploadResponse = await fileManager.uploadFile(path, {   //give the path as an argument
      mimeType: videoData.mimetype,  
      displayName: videoData.name,
    });
    const name = uploadResponse.file.name;
    let file = await fileManager.getFile(name);    
    while (file.state === FileState.PROCESSING) {     //check the state of the file
      process.stdout.write(".");
      await new Promise((res) => setTimeout(res, 10000));   //check every 10 second
      file = await fileManager.getFile(name);
    }
    if (file.state === FileState.FAILED) {   
      throw new Error("Video processing failed");
    }
    return file;   // return the file object, containing the upload file information and the uri
  } catch (error) {
    throw error;
  }
}

在上面的代码中,我们创建了一个名为fileUpload的函数,它带有两个参数。这些参数将从控制器函数传递,我们稍后将对其进行设置。

fileUpload函数使用fileManager.uploadFile方法将视频发送到 Google 的服务器。此方法需要两个参数:文件路径和包含文件元数据(其 MIME 类型和显示名称)的对象。

由于 Google 服务器上的视频处理需要时间,因此我们需要检查文件的状态。我们使用一个循环来执行此操作,该循环使用fileManager.getFile()每 10 秒检查一次文件的状态。只要文件的状态为PROCESSING循环就会继续。一旦状态更改为SUCCESS或FAILED ,循环就会停止。

然后该函数检查处理是否成功。如果是,则返回文件对象,其中包含有关上传和处理的视频的信息,包括其 URI。否则,如果状态为FAILED ,该函数将引发错误。

将 URI 传递给 Gemini 模型

在utils文件夹中,创建一个名为genContent.js的文件:

// server/utils/genContent.js

import model from "../gemini/gemini.config.js";
import { configDotenv } from "dotenv";
configDotenv();

export async function getContent(file) {
  try {
    const result = await model.generateContent([
      {
        fileData: {
          mimeType: file.mimeType,
          fileUri: file.uri,
        },
      },
      {
        text: "You need to write a subtitle for this full video, write the subtitle in the SRT format, don't write anything else other than a subtitle in the response, create accurate subtitle.",
      },
    ]);
    return result.response.text();
  } catch (error) {
    throw error;
  }
}

导入我们之前配置的模型。创建一个名为getContent的函数。 getContent函数获取文件对象(从fileUpload函数返回)。

将文件 URI 和mimi传递给模型。然后,我们将提供提示,指示模型为整个视频生成 SRT 格式的字幕。如果需要,还可以添加提示。然后返回响应。

更新subs.controller.js文件

最后,我们需要更新控制器文件。我们已经创建了fileUpload和getContent函数,现在我们将在控制器中使用它们并提供所需的参数。

在 server/controller/subs.controller.js :

//  server/controller/subs.controller.js

import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
import { fileUpload } from "../utils/fileUpload.js";
import { getContent } from "../utils/genContent.js";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export const uploadFile = async (req, res) => {
  try {
    if (!req.files || !req.files.video) {
      return res.status(400).json({ error: "No video uploaded" });
    }

    const videoFile = req.files.video;
    const uploadDir = path.join(__dirname, "..", "uploads");

    if (!fs.existsSync(uploadDir)) {
      fs.mkdirSync(uploadDir);
    }

    const uploadPath = path.join(uploadDir, videoFile.name);

    await videoFile.mv(uploadPath);

    const response = await fileUpload(uploadPath, req.files.video);  //we pass 'uploadPath' and the video file data to 'fileUpload'
    const genContent = await getContent(response);   //the 'response' (containing the file URI) is passed to 'getContent'

    return res.status(200).json({ subs: genContent });   //// return the generated subtitles to the client
  } catch (error) {
    console.error("Error uploading video:", error);
    return res
      .status(500)
      .json({ error: "Internal server error: " + error.message });
  }
};

至此,后台API就完成了。现在,我们将继续更新前端。

更新前端

我们的前端目前只允许用户选择视频。在本节中,我们将更新它以将视频数据发送到后端进行处理。然后,前端将从后端接收生成的字幕并启动.srt文件的下载。

导航到client文件夹:

cd client

安装axios 。我们将使用它来处理 HTTP 请求。

npm install axios

client/src/App.tsx

//   client/src/App.tsx

import axios from "axios";

const App = () => {
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
    e.preventDefault();
    try {
      const formData = new FormData(e.currentTarget);
      // sending a POST request with form data
      const response = await axios.post(
        "http://localhost:3000/api/subs/",   
        formData
      );
// creating a Blob from the server response and triggering the file download
      const blob = new Blob([response.data.subs], { type: "text/plain" }); 
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = "subtitle.srt";
      link.click();
      link.remove();
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input type="file" accept="video/*,.mkv" name="video" />
        <input type="submit" />
      </form>
    </div>
  );
};
export default App;

axios向后端 API 端点(/api/subs)发出 POST 请求。服务器将处理视频,这可能需要一些时间。

服务器发送生成的字幕后,前端接收它们作为响应。为了处理此响应并允许用户下载字幕,我们将使用 Blob。 Blob(二进制大对象)是一种 Web API 对象,表示原始二进制数据,本质上就像文件一样。在我们的例子中,从服务器返回的字幕将被转换为 Blob,然后可以在用户的浏览器中触发下载。

概括

在本教程中,学习了如何使用 Google 的 Gemini API、React 和 Express 构建人工智能驱动的字幕生成器。可以上传视频,发送到Gemini API进行字幕生成,并提供生成的字幕供下载。

结论

就是这样!已使用 Gemini API 成功构建了人工智能驱动的字幕生成器。为了更快地进行测试,请从较短的视频剪辑(3-5 分钟)开始。较长的视频可能需要更多时间来处理。

想要创建可定制的视频提示应用程序吗?只需添加一个输入字段,让用户输入提示,将该提示发送到服务器,并使用它代替硬编码的提示。仅此而已。

译者介绍

崔皓,51CTO社区编辑,资深架构师,拥有18年的软件开发和架构经验,10年分布式架构经验。


责任编辑:华轩 来源: 51CTO
相关推荐

2022-12-15 08:49:58

ReactQR生成器

2022-10-17 18:29:55

2022-11-21 17:44:03

机器学习文本生成器自然语言

2022-08-28 19:10:37

JavaScript二维码源码

2024-02-23 15:53:13

OpenAISora人工智能

2021-11-04 09:00:00

JavaSpring BootURL

2021-12-10 09:45:19

生成器配置代码

2024-02-18 08:00:00

PythonAI多模态模型API

2021-04-22 21:15:38

Generator函数生成器

2011-12-23 13:42:05

JavaScript

2010-09-07 16:31:17

SQL语句insert

2015-08-25 11:07:58

2017-07-01 16:02:39

分布式ID生成器

2024-11-01 15:51:06

2022-02-22 08:20:04

React工具PDF 文件

2023-05-17 16:02:00

CSS工具代码生成器

2021-12-04 22:07:44

Python

2015-08-25 15:54:17

程序员代码生成器

2017-09-06 09:26:03

Python生成器协程

2023-07-02 14:14:37

ChatGPTMidjourney
点赞
收藏

51CTO技术栈公众号