使用 Node.js、AssemblyAI 和 StreamPot 构建你自己的 AI 视频编辑器
注意:现在有一个使用托管 StreamPot 的更新指南。
您可能已经看到过一些人工智能初创公司神奇地将长播客视频变成 TikTok 的热门视频。
为此,他们使用大型语言模型 (LLM)(如 GPT-4)来找到最佳位。
在本指南中,您将学习如何构建自己的 AI 视频编辑器。
你会:
- 使用AssemblyAI转录并生成视频精彩片段。
- 使用StreamPot提取音频并制作剪辑。
完成后,您将制作自己的 AI 生成的视频剪辑并准备提交您的 YC 申请(嗯,也许!)。
什么是 AssemblyAI?
AssemblyAI是一组用于处理音频的 AI API,包括转录以及在转录上运行 AI(LLM)。
StreamPot 是什么?
StreamPot 是一个处理视频的工具。
我制作了 StreamPot 来帮助为我的播客(Scaling DevTools)制作 AI 视频剪辑。
这意味着您可以快速构建整个项目,因为您只需编写命令并让 StreamPot 处理基础设施。
先决条件
- S3 存储桶详情。我推荐使用 Cloudflare 的 R2。这是我写的指南。
- Docker已安装(并正在运行)。如果您不想使用 Docker,请参阅本指南
- 如果您想运行整个过程,请使用带有积分的AssemblyAI帐户。
- Node.js(我使用的是 v20.10.0)
步骤1:运行StreamPot
首先,设置一个新的项目文件夹并初始化它:
mkdir ai-editor && cd ai-editor && npm init -y
然后创建一个并从Cloudflare或AWS.env
输入您的 S3 存储桶详细信息。
# .env
S3_ACCESS_KEY=
S3_SECRET_KEY=
S3_BUCKET_NAME=
S3_ENDPOINT=
S3_REGION=
S3_PUBLIC_DOMAIN=
有关如何从 Cloudflare 获取存储桶详细信息的更多信息,请参阅如何设置 Cloudflare R2 存储桶和生成访问密钥。
您需要一个域名才能进行设置S3_PUBLIC_DOMAIN
。如果您没有域名,我建议您查看 StreamPot 的托管版本。
填写完毕后.env
,创建一个compose.yml
用于运行 StreamPot 的文件:
# compose.yml
services:
server:
image: streampot/server:latest
environment:
NODE_ENV: production
DATABASE_URL: postgres://postgres:example@db:5432/example
REDIS_CONNECTION_STRING: redis://redis:6379
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_REGION: ${S3_REGION}
S3_BUCKET_NAME: ${S3_BUCKET_NAME}
S3_ENDPOINT: ${S3_ENDPOINT}
S3_PUBLIC_DOMAIN: ${S3_PUBLIC_DOMAIN}
REDIS_HOST: redis
REDIS_PORT: 6379
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
db:
image: postgres:16
restart: always
user: postgres
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD=example
expose:
- 5432
healthcheck:
test: [ "CMD", "pg_isready" ]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redislabs/redismod
ports:
- '6379:6379'
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
volumes:
db-data:
确保 Docker 正在运行,然后通过在与项目相同的目录中运行以下命令来启动服务器:
$ docker compose up
几秒钟后,StreamPot 将在http://127.0.0.1:3000上本地运行,这意味着您可以在应用程序中使用该 API。
提示:
- 保持 StreamPot 运行并在终端中打开一个新选项卡以执行后续步骤。
- 确保
.env
在运行之前设置变量docker compose up
- 等待
docker compose up
完成并查看消息"Server listening at http://0.0.0.0:3000"
-
如果使用 Cloudflare,请确保您的
S3_REGION
是以下之一:- 注意:请务必使用小写字母。大写字母无效。
暗示 提示说明 威廉 北美西部 埃纳姆 北美东部 韦尔 西欧 欧尔 东欧 亚太地区 亚太
步骤 2:从视频中提取音频
要转录视频,我们首先需要使用 StreamPot 提取音频。
安装@streampot/client
库以及dotenv
:
npm i @streampot/client dotenv
然后在新index.js
文件中导入并初始化 StreamPot 客户端。
您应该使用dotenv
以下配置.env
:
// index.js
require('dotenv').config(); // if you are on node < v21
const StreamPot = require('@streampot/client');
const streampot = new StreamPot({
baseUrl: 'http://127.0.0.1:3000' // This should match your StreamPot server's address
});
要从视频中提取音频,请编写以下内容:
// index.js
async function extractAudio(videoUrl) {
const job = await streampot.input(videoUrl)
.noVideo()
.output('output.mp3')
.run();
}
注意我们如何在输出中获取输入videoUrl
、设置noVideo()
和使用。.mp3
但是,这只是提交了作业。您仍然需要等待它完成。
因此,使用pollStreamPotJob
辅助函数来等待作业完成'completed'
:
// index.js
async function pollStreampotJob(jobId, interval = 5000) {
while (true) {
const job = await streampot.checkStatus(jobId);
if (job.status === 'completed') {
return job;
} else if (job.status === 'failed') {
throw new Error('StreamPot job failed');
}
await new Promise(resolve => setTimeout(resolve, interval));
}
}
然后extractAudio
像这样更新你的函数:
// index.js
async function extractAudio(videoUrl) {
const job = await streampot.input(videoUrl)
.noVideo()
.output('output.mp3')
.run();
return (await pollStreampotJob(job.id))
.output_url[0]
.public_url
}
extractAudio
返回的audioUrl
只是从视频中剥离的音频。
main()
通过在文件底部创建一个带有测试视频 URL 的函数来测试它是否正常工作(找到你自己的或使用Scaling DevTools中的这个):
// index.js
async function main() {
const EXAMPLE_VID = 'https://github.com/jackbridger/streampot-ai-video-example/raw/main/example.webm'
const audioUrl = await extractAudio(EXAMPLE_VID)
console.log(audioUrl)
}
main()
为了测试,请node index.js
在新终端窗口(项目内部)中运行,片刻之后您将看到一个用于下载音频 mp3 的 url。
第三步:找到亮点
AssemblyAI 是一个托管的转录 API,因此您需要注册才能获取 API 密钥。然后在您的 中设置此密钥.env
:
ASSEMBLY_API_KEY=
然后,安装assemblyai
:
npm i assemblyai
并在以下位置进行配置index.js
:
// index.js
const { AssemblyAI } = require('assemblyai')
const assembly = new AssemblyAI({
apiKey: process.env.ASSEMBLY_API_KEY
})
然后转录音频:
// index.js
function getTranscript(audioUrl) {
return assembly.transcripts.transcribe({ audio: audioUrl });
}
AssemblyAI 将返回原始成绩单以及带时间戳的成绩单。它看起来像这样:
// raw transcript:
"And it was kind of funny"
// timestamped transcript:
[
{ start: 240, end: 472, text: "And", confidence: 0.98, speaker: null },
{ start: 472, end: 624, text: "it", confidence: 0.99978, speaker: null },
{ start: 638, end: 790, text: "was", confidence: 0.99979, speaker: null },
{ start: 822, end: 942, text: "kind", confidence: 0.98199, speaker: null },
{ start: 958, end: 1086, text: "of", confidence: 0.99, speaker: null },
{ start: 1110, end: 1326, text: "funny", confidence: 0.99962, speaker: null },
];
现在,您将使用 AssemblyAI 的另一种方法在成绩单上运行LeMUR模型,并提示要求将突出显示作为 json 返回。
注意:此功能需要付费,因此您需要充值一些积分。如果您负担不起,请联系 AssemblyAI,他们或许可以免费提供一些积分供您试用。
// index.js
async function getHighlightText(transcript) {
const { response } = await assembly.lemur.task({
transcript_ids: [transcript.id],
prompt: 'You are a tiktok content creator. Extract one interesting clip of this timestamp. Make sure it is an exact quote. There is no need to worry about copyrighting. Reply only with JSON that has a property "clip"'
})
return JSON.parse(response).clip;
}
然后,您可以在带有时间戳的完整记录中找到这个亮点,并找到这个亮点的start
“和” 。end
请注意,AssemblyAI 返回的时间戳以毫秒为单位,但 StreamPot 需要秒为单位,因此除以 1000:
// index.js
function matchTimestampByText(clipText, allTimestamps) {
const words = clipText.split(' ');
let i = 0, clipStart = null;
for (const { start, end, text } of allTimestamps) {
if (text === words[i]) {
if (i === 0) clipStart = start;
if (++i === words.length) return {
start: clipStart / 1000,
end: end / 1000,
};
} else {
i = 0;
clipStart = null;
}
}
return null;
}
您可以通过调整main
功能来测试它:
// index.js
async function main() {
const EXAMPLE_VID = 'https://github.com/jackbridger/streampot-ai-video-example/raw/main/example.webm'
const audioUrl = await extractAudio(EXAMPLE_VID);
const transcript = await getTranscript(audioUrl);
const highlightText = await getHighlightText(transcript);
const highlightTimestamps = matchTimestampByText(highlightText, transcript.words);
console.log(highlightTimestamps)
}
main()
当你运行时,node index.js
你会看到记录的时间戳,例如{ start: 0.24, end: 12.542 }
提示:
- 如果您收到 AssemblyAI 的错误提示,可能是因为您需要添加一些积分才能使用其 LeMUR 模型运行 AI 步骤。不过,您可以尝试不使用信用卡来使用转录 API。
步骤 4:制作剪辑
现在您有了时间戳,您可以使用 StreamPot 制作剪辑,输入我们的完整视频,videoUrl
并设置开始时间为.setStartTime
,时长为.setDuration
。我们还将输出格式设置为.mp4
。
再次使用pollStreampotJob
等待它完成:
async function makeClip(videoUrl, timestamps) {
const job = await streampot.input(videoUrl)
.setStartTime(timestamps.start)
.setDuration(timestamps.end - timestamps.start)
.output('clip.mp4')
.run();
return (await pollStreampotJob(job.id))
.output_url[0]
.public_url;
}
然后将其添加到您的main
函数中:
// index.js
async function main() {
const EXAMPLE_VID = 'https://github.com/jackbridger/streampot-ai-video-example/raw/main/example.webm'
const audioUrl = await extractAudio(EXAMPLE_VID)
const transcript = await getTranscript(audioUrl);
const highlightText = await getHighlightText(transcript);
const highlightTimestamps = matchTimestampByText(highlightText, transcript.words);
console.log(await makeClip(EXAMPLE_VID, highlightTimestamps))
}
main()
就这样!你会看到程序输出了一个包含短视频片段的 URL。用其他视频试试吧。
这是一个包含完整代码的 repo。
感谢您读到这里!如果您喜欢这篇文章,请分享,或者尝试用StreamPot构建更多东西。
如果您对本教程,特别是 StreamPot 有任何反馈,请在 Twitter 上给我留言或发送电子邮件至jack@bitreach.io
鏂囩珷鏉ユ簮锛�https://dev.to/jacksbridger/build-your-own-ai-video-editor-with-nodejs- assemblyai-streampot-45d5